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