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