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