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