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