core/sm_stringutil.cpp
author David Anderson <dvander@alliedmods.net>
Wed Feb 07 08:44:48 2007 +0000 (2007-02-07)
changeset 448 25984ddd0ce0
parent 422 5ef0f5610c2a
child 457 9b8a85bf4175
permissions -rw-r--r--
added steamid auth functions
added better bot detection
added gameframe hook for timer
renamed more playermanager stuff
     1 /**
     2  * ===============================================================
     3  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     4  * ===============================================================
     5  *
     6  * This file is not open source and may not be copied without explicit
     7  * written permission of AlliedModders LLC.  This file may not be redistributed 
     8  * in whole or significant part.
     9  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    10  *
    11  * Version: $Id$
    12  */
    13 
    14 #include <stdlib.h>
    15 #include <ctype.h>
    16 #include <stdarg.h>
    17 #include "sm_stringutil.h"
    18 #include "CLogger.h"
    19 #include "PluginSys.h"
    20 #include "CTranslator.h"
    21 #include "CPlayerManager.h"
    22 
    23 #define LADJUST			0x00000004		/* left adjustment */
    24 #define ZEROPAD			0x00000080		/* zero (as opposed to blank) pad */
    25 #define to_digit(c)		((c) - '0')
    26 #define is_digit(c)		((unsigned)to_digit(c) <= 9)
    27 
    28 #define CHECK_ARGS(x) \
    29 	if ((arg+x) > args) { \
    30 		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
    31 		return 0; \
    32 	}
    33 
    34 inline bool TryServerLanguage(const char *serverlang, unsigned int *langid)
    35 {
    36 	if (!g_Translator.GetLanguageByCode(serverlang, langid))
    37 	{
    38 		if (!g_Translator.GetLanguageByCode("en", langid))
    39 		{
    40 			return false;
    41 		}
    42 	}
    43 	return true;
    44 }
    45 
    46 inline bool TryTranslation(CPlugin *pl, const char *key, unsigned int langid, unsigned int langcount, Translation *pTrans)
    47 {
    48 	TransError err = Trans_BadLanguage;
    49 	CPhraseFile *phrfl;
    50 
    51 	for (size_t i=0; i<langcount && err!=Trans_Okay; i++)
    52 	{
    53 		phrfl = g_Translator.GetFileByIndex(pl->GetFileByIndex(i));
    54 		err = phrfl->GetTranslation(key, langid, pTrans);
    55 	}
    56 
    57 	return (err == Trans_Okay) ? true : false;
    58 }
    59 
    60 size_t Translate(char *buffer, size_t maxlen, IPluginContext *pCtx, const char *key, cell_t target, const cell_t *params, int *arg, bool *error)
    61 {
    62 	unsigned int langid;
    63 	char *langname = NULL;
    64 	*error = false;
    65 
    66 try_serverlang:
    67 	if (target == LANG_SERVER)
    68 	{
    69 		langname = "en"; //:TODO: read serverlang
    70 		if (!TryServerLanguage(langname ? langname : "en", &langid))
    71 		{
    72 			pCtx->ThrowNativeError("Translation failure: English language not found.");
    73 			goto error_out;
    74 		}
    75 	} else if ((target >= 1) && (target <= g_Players.GetMaxClients())) {
    76 		langname = "en"; //:TODO: read player's lang
    77 		if (!langname || !g_Translator.GetLanguageByCode(langname, &langid))
    78 		{
    79 			if (langname && !strcmp(langname, "en"))
    80 			{
    81 				pCtx->ThrowNativeError("Translation failure: English language not found.");
    82 				goto error_out;
    83 			}
    84 			target = LANG_SERVER;
    85 			goto try_serverlang;
    86 		}
    87 	} else {
    88 		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
    89 		goto error_out;
    90 	}
    91 
    92 	Translation pTrans;
    93 	CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
    94 	size_t langcount = pl->GetLangFileCount();
    95 
    96 	if (!TryTranslation(pl, key, langid, langcount, &pTrans))
    97 	{
    98 		if (target != LANG_SERVER)
    99 		{
   100 			target = LANG_SERVER;
   101 			goto try_serverlang;
   102 		} else {
   103 			if (!g_Translator.GetLanguageByCode("en", &langid)
   104 				|| !TryTranslation(pl, key, langid, langcount, &pTrans))
   105 			{
   106 				pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
   107 				goto error_out;
   108 			}
   109 		}
   110 	}
   111 
   112 	void *new_params[MAX_TRANSLATE_PARAMS];
   113 	unsigned int max_params = pTrans.fmt_count;
   114 	for (size_t i=0; i<max_params; i++)
   115 	{
   116 		pCtx->LocalToPhysAddr(params[*arg], reinterpret_cast<cell_t **>(&new_params[i]));
   117 		(*arg)++;
   118 		if ((*arg) + i > (size_t)params[0])
   119 		{
   120 			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, "Translation string formatted incorrectly - parameter %d (total %d)", (*arg) + i, params[0]);
   121 			goto error_out;
   122 		}
   123 	}
   124 
   125 	return g_Translator.Translate(buffer, maxlen, new_params, &pTrans);
   126 error_out:
   127 	*error = true;
   128 	return 0;
   129 }
   130 
   131 //:TODO: review this code before we choose a license
   132 
   133 void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
   134 {
   135 	int size = 0;
   136 	char *buf;
   137 	static char nlstr[] = {'(','n','u','l','l',')','\0'};
   138 
   139 	buf = *buf_p;
   140 
   141 	if (string == NULL)
   142 	{
   143 		string = nlstr;
   144 		prec = -1;
   145 	}
   146 
   147 	if (prec >= 0)
   148 	{
   149 		for (size = 0; size < prec; size++) 
   150 		{
   151 			if (string[size] == '\0')
   152 			{
   153 				break;
   154 			}
   155 		}
   156 	} else {
   157 		while (string[size++]);
   158 		size--;
   159 	}
   160 
   161 	if (size > (int)maxlen)
   162 	{
   163 		size = maxlen;
   164 	}
   165 
   166 	maxlen -= size;
   167 	width -= size;
   168 
   169 	while (size--)
   170 	{
   171 		*buf++ = *string++;
   172 	}
   173 
   174 	while ((width-- > 0) && maxlen)
   175 	{
   176 		*buf++ = ' ';
   177 		maxlen--;
   178 	}
   179 
   180 	*buf_p = buf;
   181 }
   182 
   183 void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec)
   184 {
   185 	char text[32];
   186 	int digits;
   187 	double signedVal;
   188 	char *buf;
   189 	int val;
   190 
   191 	// get the sign
   192 	signedVal = fval;
   193 	if (fval < 0)
   194 	{
   195 		fval = -fval;
   196 	}
   197 
   198 	// write the float number
   199 	digits = 0;
   200 	val = (int)fval;
   201 	do
   202 	{
   203 		text[digits++] = '0' + val % 10;
   204 		val /= 10;
   205 	} while (val);
   206 
   207 	if (signedVal < 0)
   208 	{
   209 		text[digits++] = '-';
   210 	}
   211 
   212 	buf = *buf_p;
   213 
   214 	while ((digits < width) && maxlen)
   215 	{
   216 		*buf++ = ' ';
   217 		width--;
   218 		maxlen--;
   219 	}
   220 
   221 	while ((digits--) && maxlen)
   222 	{
   223 		*buf++ = text[digits];
   224 		maxlen--;
   225 	}
   226 
   227 	*buf_p = buf;
   228 
   229 	if (prec < 0)
   230 	{
   231 		prec = 6;
   232 	}
   233 	// write the fraction
   234 	digits = 0;
   235 	while (digits < prec)
   236 	{
   237 		fval -= (int)fval;
   238 		fval *= 10.0;
   239 		val = (int)fval;
   240 		text[digits++] = '0' + val % 10;
   241 	}
   242 
   243 	if ((digits > 0) && maxlen)
   244 	{
   245 		buf = *buf_p;
   246 		*buf++ = '.';
   247 		maxlen--;
   248 		for (prec = 0; maxlen && (prec < digits); prec++)
   249 		{
   250 			*buf++ = text[prec];
   251 			maxlen--;
   252 		}
   253 		*buf_p = buf;
   254 	}
   255 }
   256 
   257 void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   258 {
   259 	char text[32];
   260 	int digits;
   261 	char *buf;
   262 
   263 	digits = 0;
   264 	do
   265 	{
   266 		text[digits++] = '0' + val % 10;
   267 		val /= 10;
   268 	} while (val);
   269 
   270 	buf = *buf_p;
   271 
   272 	if (!(flags & LADJUST))
   273 	{
   274 		while (digits < width && maxlen)
   275 		{
   276 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   277 			width--;
   278 			maxlen--;
   279 		}
   280 	}
   281 
   282 	while (digits-- && maxlen)
   283 	{
   284 		*buf++ = text[digits];
   285 		width--;
   286 		maxlen--;
   287 	}
   288 
   289 	if (flags & LADJUST)
   290 	{
   291 		while (width-- && maxlen)
   292 		{
   293 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   294 			maxlen--;
   295 		}
   296 	}
   297 
   298 	*buf_p = buf;
   299 }
   300 
   301 void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
   302 {
   303 	char text[32];
   304 	int digits;
   305 	int signedVal;
   306 	char *buf;
   307 	unsigned int unsignedVal;
   308 
   309 	digits = 0;
   310 	signedVal = val;
   311 	if (val < 0)
   312 	{
   313 		/* we want the unsigned version */
   314 		unsignedVal = abs(val);
   315 	} else {
   316 		unsignedVal = val;
   317 	}
   318 	do
   319 	{
   320 		text[digits++] = '0' + unsignedVal % 10;
   321 		unsignedVal /= 10;
   322 	} while (unsignedVal);
   323 
   324 	if (signedVal < 0)
   325 	{
   326 		text[digits++] = '-';
   327 	}
   328 
   329 	buf = *buf_p;
   330 
   331 	if (!(flags & LADJUST))
   332 	{
   333 		while ((digits < width) && maxlen)
   334 		{
   335 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   336 			width--;
   337 			maxlen--;
   338 		}
   339 	}
   340 
   341 	while (digits-- && maxlen)
   342 	{
   343 		*buf++ = text[digits];
   344 		width--;
   345 		maxlen--;
   346 	}
   347 
   348 	if (flags & LADJUST)
   349 	{
   350 		while (width-- && maxlen)
   351 		{
   352 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   353 			maxlen--;
   354 		}
   355 	}
   356 
   357 	*buf_p = buf;
   358 }
   359 
   360 size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
   361 {
   362 	if (!buffer || !maxlen)
   363 	{
   364 		return 0;
   365 	}
   366 
   367 	int arg = 0;
   368 	char *buf_p;
   369 	char ch;
   370 	int flags;
   371 	int width;
   372 	int prec;
   373 	int n;
   374 	char sign;
   375 	const char *fmt;
   376 	size_t llen = maxlen - 1;
   377 
   378 	buf_p = buffer;
   379 	fmt = format;
   380 
   381 	while (true)
   382 	{
   383 		// run through the format string until we hit a '%' or '\0'
   384 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
   385 		{
   386 			*buf_p++ = ch;
   387 			llen--;
   388 		}
   389 		if ((ch == '\0') || (llen <= 0))
   390 		{
   391 			goto done;
   392 		}
   393 
   394 		// skip over the '%'
   395 		fmt++;
   396 
   397 		// reset formatting state
   398 		flags = 0;
   399 		width = 0;
   400 		prec = -1;
   401 		sign = '\0';
   402 
   403 rflag:
   404 		ch = *fmt++;
   405 reswitch:
   406 		switch(ch)
   407 		{
   408 		case '-':
   409 			{
   410 				flags |= LADJUST;
   411 				goto rflag;
   412 			}
   413 		case '.':
   414 			{
   415 				n = 0;
   416 				while(is_digit((ch = *fmt++)))
   417 				{
   418 					n = 10 * n + (ch - '0');
   419 				}
   420 				prec = (n < 0) ? -1 : n;
   421 				goto reswitch;
   422 			}
   423 		case '0':
   424 			{
   425 				flags |= ZEROPAD;
   426 				goto rflag;
   427 			}
   428 		case '1':
   429 		case '2':
   430 		case '3':
   431 		case '4':
   432 		case '5':
   433 		case '6':
   434 		case '7':
   435 		case '8':
   436 		case '9':
   437 			{
   438 				n = 0;
   439 				do
   440 				{
   441 					n = 10 * n + (ch - '0');
   442 					ch = *fmt++;
   443 				} while(is_digit(ch));
   444 				width = n;
   445 				goto reswitch;
   446 			}
   447 		case 'c':
   448 			{
   449 				if (!llen)
   450 				{
   451 					goto done;
   452 				}
   453 				char *c = (char *)args[arg];
   454 				*buf_p++ = *c;
   455 				llen--;
   456 				arg++;
   457 				break;
   458 			}
   459 		case 'd':
   460 		case 'i':
   461 			{
   462 				int *value = (int *)args[arg];
   463 				AddInt(&buf_p, llen, *value, width, flags);
   464 				arg++;
   465 				break;
   466 			}
   467 		case 'u':
   468 			{
   469 				unsigned int *value = (unsigned int *)args[arg];
   470 				AddUInt(&buf_p, llen, *value, width, flags);
   471 				arg++;
   472 				break;
   473 			}
   474 		case 'f':
   475 			{
   476 				float *value = (float *)args[arg];
   477 				AddFloat(&buf_p, llen, *value, width, prec);
   478 				arg++;
   479 				break;
   480 			}
   481 		case 's':
   482 			{
   483 				const char *str = (const char *)args[arg];
   484 				AddString(&buf_p, llen, str, width, prec);
   485 				arg++;
   486 				break;
   487 			}
   488 		case '%':
   489 			{
   490 				if (!llen)
   491 				{
   492 					goto done;
   493 				}
   494 				*buf_p++ = ch;
   495 				llen--;
   496 				break;
   497 			}
   498 		case '\0':
   499 			{
   500 				if (!llen)
   501 				{
   502 					goto done;
   503 				}
   504 				*buf_p++ = '%';
   505 				llen--;
   506 				goto done;
   507 			}
   508 		default:
   509 			{
   510 				if (!llen)
   511 				{
   512 					goto done;
   513 				}
   514 				*buf_p++ = ch;
   515 				llen--;
   516 				break;
   517 			}
   518 		}
   519 	}
   520 
   521 done:
   522 	*buf_p = '\0';
   523 
   524 	return (maxlen - llen - 1);
   525 }
   526 
   527 size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
   528 {
   529 	if (!buffer || !maxlen)
   530 	{
   531 		return 0;
   532 	}
   533 
   534 	int arg;
   535 	int args = params[0];
   536 	char *buf_p;
   537 	char ch;
   538 	int flags;
   539 	int width;
   540 	int prec;
   541 	int n;
   542 	char sign;
   543 	const char *fmt;
   544 	size_t llen = maxlen - 1;
   545 
   546 	buf_p = buffer;
   547 	arg = *param;
   548 	fmt = format;
   549 
   550 	while (true)
   551 	{
   552 		// run through the format string until we hit a '%' or '\0'
   553 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
   554 		{
   555 			*buf_p++ = ch;
   556 			llen--;
   557 		}
   558 		if ((ch == '\0') || (llen <= 0))
   559 		{
   560 			goto done;
   561 		}
   562 
   563 		// skip over the '%'
   564 		fmt++;
   565 
   566 		// reset formatting state
   567 		flags = 0;
   568 		width = 0;
   569 		prec = -1;
   570 		sign = '\0';
   571 
   572 rflag:
   573 		ch = *fmt++;
   574 reswitch:
   575 		switch(ch)
   576 		{
   577 		case '-':
   578 			{
   579 				flags |= LADJUST;
   580 				goto rflag;
   581 			}
   582 		case '.':
   583 			{
   584 				n = 0;
   585 				while(is_digit((ch = *fmt++)))
   586 				{
   587 					n = 10 * n + (ch - '0');
   588 				}
   589 				prec = (n < 0) ? -1 : n;
   590 				goto reswitch;
   591 			}
   592 		case '0':
   593 			{
   594 				flags |= ZEROPAD;
   595 				goto rflag;
   596 			}
   597 		case '1':
   598 		case '2':
   599 		case '3':
   600 		case '4':
   601 		case '5':
   602 		case '6':
   603 		case '7':
   604 		case '8':
   605 		case '9':
   606 			{
   607 				n = 0;
   608 				do
   609 				{
   610 					n = 10 * n + (ch - '0');
   611 					ch = *fmt++;
   612 				} while(is_digit(ch));
   613 				width = n;
   614 				goto reswitch;
   615 			}
   616 		case 'c':
   617 			{
   618 				CHECK_ARGS(0);
   619 				if (!llen)
   620 				{
   621 					goto done;
   622 				}
   623 				char *c;
   624 				pCtx->LocalToString(params[arg], &c);
   625 				*buf_p++ = *c;
   626 				llen--;
   627 				arg++;
   628 				break;
   629 			}
   630 		case 'd':
   631 		case 'i':
   632 			{
   633 				CHECK_ARGS(0);
   634 				cell_t *value;
   635 				pCtx->LocalToPhysAddr(params[arg], &value);
   636 				AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
   637 				arg++;
   638 				break;
   639 			}
   640 		case 'u':
   641 			{
   642 				CHECK_ARGS(0);
   643 				cell_t *value;
   644 				pCtx->LocalToPhysAddr(params[arg], &value);
   645 				AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   646 				arg++;
   647 				break;
   648 			}
   649 		case 'f':
   650 			{
   651 				CHECK_ARGS(0);
   652 				cell_t *value;
   653 				pCtx->LocalToPhysAddr(params[arg], &value);
   654 				AddFloat(&buf_p, llen, sp_ctof(*value), width, prec);
   655 				arg++;
   656 				break;
   657 			}
   658 		case 's':
   659 			{
   660 				CHECK_ARGS(0);
   661 				char *str;
   662 				pCtx->LocalToString(params[arg], &str);
   663 				AddString(&buf_p, llen, str, width, prec);
   664 				arg++;
   665 				break;
   666 			}
   667 		case 'T':
   668 			{
   669 				CHECK_ARGS(0);
   670 				char *key;
   671 				bool error;
   672 				size_t res;
   673 				cell_t target = params[arg++];
   674 				pCtx->LocalToString(params[arg++], &key);
   675 				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
   676 				if (error)
   677 				{
   678 					return 0;
   679 				}
   680 				buf_p += res;
   681 				llen -= res;
   682 				break;
   683 			}
   684 		case 't':
   685 			{
   686 				CHECK_ARGS(0);
   687 				char *key;
   688 				bool error;
   689 				size_t res;
   690 				cell_t target = g_SourceMod.GetGlobalTarget();
   691 				pCtx->LocalToString(params[arg++], &key);
   692 				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
   693 				if (error)
   694 				{
   695 					return 0;
   696 				}
   697 				buf_p += res;
   698 				llen -= res;
   699 				break;
   700 			}
   701 		case '%':
   702 			{
   703 				if (!llen)
   704 				{
   705 					goto done;
   706 				}
   707 				*buf_p++ = ch;
   708 				llen--;
   709 				break;
   710 			}
   711 		case '\0':
   712 			{
   713 				if (!llen)
   714 				{
   715 					goto done;
   716 				}
   717 				*buf_p++ = '%';
   718 				llen--;
   719 				goto done;
   720 			}
   721 		default:
   722 			{
   723 				if (!llen)
   724 				{
   725 					goto done;
   726 				}
   727 				*buf_p++ = ch;
   728 				llen--;
   729 				break;
   730 			}
   731 		}
   732 	}
   733 
   734 done:
   735 	*buf_p = '\0';
   736 	*param = arg;
   737 	return (maxlen - llen - 1);
   738 }
   739 
   740 const char *stristr(const char *str, const char *substr)
   741 {
   742 	if (!*substr)
   743 	{
   744 		return ((char *)str);
   745 	}
   746 
   747 	char *needle = (char *)substr;
   748 	char *prevloc = (char *)str;
   749 	char *haystack = (char *)str;
   750 
   751 	while (*haystack)
   752 	{
   753 		if (tolower(*haystack) == tolower(*needle))
   754 		{
   755 			haystack++;
   756 			if (!*++needle)
   757 			{
   758 				return prevloc;
   759 			}
   760 		} else {
   761 			haystack = ++prevloc;
   762 			needle = (char *)substr;
   763 		}
   764 	}
   765 
   766 	return NULL;
   767 }
   768 
   769 unsigned int strncopy(char *dest, const char *src, size_t count)
   770 {
   771 	if (!count)
   772 	{
   773 		return 0;
   774 	}
   775 
   776 	char *start = dest;
   777 	while ((*src) && (--count))
   778 	{
   779 		*dest++ = *src++;
   780 	}
   781 	*dest = '\0';
   782 
   783 	return (dest - start);
   784 }
   785 
   786 size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
   787 {
   788 	va_list ap;
   789 	va_start(ap, fmt);
   790 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
   791 	va_end(ap);
   792 
   793 	return (len >= maxlength) ? (maxlength - 1) : len;
   794 }
   795