core/sm_stringutil.cpp
author David Anderson <dvander@alliedmods.net>
Sun Apr 06 06:56:45 2008 +0000 (2008-04-06)
changeset 1965 63d5a5f6935f
parent 1952 b7aa2b626807
child 2042 4a2faafca477
permissions -rw-r--r--
merged changes back from 1.0.1
ds@1952
     1
/**
ds@1952
     2
 * vim: set ts=4 :
ds@1952
     3
 * =============================================================================
ds@1952
     4
 * SourceMod
ds@1952
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
ds@1952
     6
 * =============================================================================
ds@1952
     7
 *
ds@1952
     8
 * This program is free software; you can redistribute it and/or modify it under
ds@1952
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
ds@1952
    10
 * Free Software Foundation.
ds@1952
    11
 * 
ds@1952
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1952
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1952
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
ds@1952
    15
 * details.
ds@1952
    16
 *
ds@1952
    17
 * You should have received a copy of the GNU General Public License along with
ds@1952
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
ds@1952
    19
 *
ds@1952
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
ds@1952
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1952
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1952
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
ds@1952
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
ds@1952
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
ds@1952
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1952
    27
 * or <http://www.sourcemod.net/license.php>.
ds@1952
    28
 *
ds@1952
    29
 * Version: $Id$
ds@1952
    30
 */
ds@1952
    31
ds@1952
    32
#include <stdlib.h>
ds@1952
    33
#include <ctype.h>
ds@1952
    34
#include <stdarg.h>
ds@1952
    35
#include "sm_stringutil.h"
ds@1952
    36
#include "Logger.h"
ds@1952
    37
#include "PluginSys.h"
ds@1952
    38
#include "Translator.h"
ds@1952
    39
#include "PlayerManager.h"
ds@1952
    40
ds@1952
    41
#define LADJUST			0x00000004		/* left adjustment */
ds@1952
    42
#define ZEROPAD			0x00000080		/* zero (as opposed to blank) pad */
ds@1952
    43
#define UPPERDIGITS		0x00000200		/* make alpha digits uppercase */
ds@1952
    44
#define to_digit(c)		((c) - '0')
ds@1952
    45
#define is_digit(c)		((unsigned)to_digit(c) <= 9)
ds@1952
    46
ds@1952
    47
#define CHECK_ARGS(x) \
ds@1952
    48
	if ((arg+x) > args) { \
ds@1952
    49
		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
ds@1952
    50
		return 0; \
ds@1952
    51
	}
ds@1952
    52
ds@1952
    53
size_t CorePlayerTranslate(int client, char *buffer, size_t maxlength, const char *phrase, void **params)
ds@1952
    54
{
ds@1952
    55
	Translation pTrans;
ds@1952
    56
	TransError err;
ds@1952
    57
ds@1952
    58
	err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetClientLanguage(client), &pTrans);
ds@1952
    59
	if (err != Trans_Okay)
ds@1952
    60
	{
ds@1952
    61
		err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetServerLanguage(), &pTrans);
ds@1952
    62
		if (err != Trans_Okay && g_Translator.GetServerLanguage() != CORELANG_ENGLISH)
ds@1952
    63
		{
ds@1952
    64
			err = g_pCorePhrases->GetTranslation(phrase, CORELANG_ENGLISH, &pTrans);
ds@1952
    65
		}
ds@1952
    66
	}
ds@1952
    67
ds@1952
    68
	if (err != Trans_Okay)
ds@1952
    69
	{
ds@1952
    70
		return UTIL_Format(buffer, maxlength, "%s", phrase);
ds@1952
    71
	}
ds@1952
    72
ds@1952
    73
	return g_Translator.Translate(buffer, maxlength, params, &pTrans);
ds@1952
    74
}
ds@1952
    75
ds@1952
    76
inline bool TryTranslation(CPlugin *pl, const char *key, unsigned int langid, unsigned int langcount, Translation *pTrans)
ds@1952
    77
{
ds@1952
    78
	TransError err = Trans_BadLanguage;
ds@1952
    79
	CPhraseFile *phrfl;
ds@1952
    80
ds@1952
    81
	for (size_t i=0; i<langcount && err!=Trans_Okay; i++)
ds@1952
    82
	{
ds@1952
    83
		phrfl = g_Translator.GetFileByIndex(pl->GetLangFileByIndex(i));
ds@1952
    84
		err = phrfl->GetTranslation(key, langid, pTrans);
ds@1952
    85
	}
ds@1952
    86
ds@1952
    87
	return (err == Trans_Okay) ? true : false;
ds@1952
    88
}
ds@1952
    89
ds@1952
    90
inline void ReorderTranslationParams(const Translation *pTrans, cell_t *params)
ds@1952
    91
{
ds@1952
    92
	cell_t new_params[MAX_TRANSLATE_PARAMS];
ds@1952
    93
	for (unsigned int i = 0; i < pTrans->fmt_count; i++)
ds@1952
    94
	{
ds@1952
    95
		new_params[i] = params[pTrans->fmt_order[i]];
ds@1952
    96
	}
ds@1952
    97
	memcpy(params, new_params, pTrans->fmt_count * sizeof(cell_t));
ds@1952
    98
}
ds@1952
    99
ds@1952
   100
size_t Translate(char *buffer,
ds@1952
   101
				 size_t maxlen,
ds@1952
   102
				 IPluginContext *pCtx,
ds@1952
   103
				 const char *key,
ds@1952
   104
				 cell_t target,
ds@1952
   105
				 const cell_t *params,
ds@1952
   106
				 int *arg,
ds@1952
   107
				 bool *error)
ds@1952
   108
{
ds@1952
   109
	unsigned int langid;
ds@1952
   110
	*error = false;
ds@1952
   111
	Translation pTrans;
ds@1952
   112
	CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
ds@1952
   113
	size_t langcount = pl->GetLangFileCount();
ds@1952
   114
	unsigned int max_params = 0;
ds@1952
   115
ds@1952
   116
try_serverlang:
ds@1952
   117
	if (target == LANG_SERVER)
ds@1952
   118
	{
ds@1952
   119
		langid = g_Translator.GetServerLanguage();
ds@1952
   120
 	}
ds@1952
   121
	else if ((target >= 1) && (target <= g_Players.GetMaxClients()))
ds@1952
   122
	{
ds@1952
   123
		langid = g_Translator.GetClientLanguage(target);
ds@1952
   124
	}
ds@1952
   125
	else
ds@1952
   126
	{
ds@1952
   127
		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
ds@1952
   128
		goto error_out;
ds@1952
   129
	}
ds@1952
   130
ds@1952
   131
	if (!TryTranslation(pl, key, langid, langcount, &pTrans))
ds@1952
   132
	{
ds@1952
   133
		if (target != LANG_SERVER && langid != g_Translator.GetServerLanguage())
ds@1952
   134
		{
ds@1952
   135
			target = LANG_SERVER;
ds@1952
   136
			goto try_serverlang;
ds@1952
   137
		}
ds@1952
   138
		else if (langid != LANGUAGE_ENGLISH)
ds@1952
   139
		{
ds@1952
   140
			if (!TryTranslation(pl, key, LANGUAGE_ENGLISH, langcount, &pTrans))
ds@1952
   141
			{
ds@1952
   142
				pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
ds@1952
   143
				goto error_out;
ds@1952
   144
			}
ds@1952
   145
		}
ds@1952
   146
		else
ds@1952
   147
		{
ds@1952
   148
			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
ds@1952
   149
			goto error_out;
ds@1952
   150
		}
ds@1952
   151
	}
ds@1952
   152
ds@1952
   153
	max_params = pTrans.fmt_count;
ds@1952
   154
ds@1952
   155
	if (max_params)
ds@1952
   156
	{
ds@1952
   157
		cell_t new_params[MAX_TRANSLATE_PARAMS];
ds@1952
   158
ds@1952
   159
		/* Check if we're going to over the limit */
ds@1952
   160
		if ((*arg) + (max_params - 1) > (size_t)params[0])
ds@1952
   161
		{
ds@1952
   162
			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, 
ds@1952
   163
				"Translation string formatted incorrectly - missing at least %d parameters", 
ds@1952
   164
				((*arg + (max_params - 1)) - params[0])
ds@1952
   165
				);
ds@1952
   166
			goto error_out;
ds@1952
   167
		}
ds@1952
   168
ds@1952
   169
		/* If we need to re-order the parameters, do so with a temporary array.
ds@1952
   170
		 * Otherwise, we could run into trouble with continual formats, a la ShowActivity().
ds@1952
   171
		 */
ds@1952
   172
		memcpy(new_params, params, sizeof(cell_t) * (params[0] + 1));
ds@1952
   173
		ReorderTranslationParams(&pTrans, &new_params[*arg]);
ds@1952
   174
ds@1952
   175
		return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, new_params, arg);
ds@1952
   176
	}
ds@1952
   177
	else
ds@1952
   178
	{
ds@1952
   179
		return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, params, arg);
ds@1952
   180
	}
ds@1952
   181
	
ds@1952
   182
error_out:
ds@1952
   183
	*error = true;
ds@1952
   184
	return 0;
ds@1952
   185
}
ds@1952
   186
ds@1952
   187
void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
ds@1952
   188
{
ds@1952
   189
	int size = 0;
ds@1952
   190
	char *buf;
ds@1952
   191
	static char nlstr[] = {'(','n','u','l','l',')','\0'};
ds@1952
   192
ds@1952
   193
	buf = *buf_p;
ds@1952
   194
ds@1952
   195
	if (string == NULL)
ds@1952
   196
	{
ds@1952
   197
		string = nlstr;
ds@1952
   198
		prec = -1;
ds@1952
   199
	}
ds@1952
   200
ds@1952
   201
	if (prec >= 0)
ds@1952
   202
	{
ds@1952
   203
		for (size = 0; size < prec; size++) 
ds@1952
   204
		{
ds@1952
   205
			if (string[size] == '\0')
ds@1952
   206
			{
ds@1952
   207
				break;
ds@1952
   208
			}
ds@1952
   209
		}
ds@1952
   210
	}
ds@1952
   211
	else
ds@1952
   212
	{
ds@1952
   213
		while (string[size++]);
ds@1952
   214
		size--;
ds@1952
   215
	}
ds@1952
   216
ds@1952
   217
	if (size > (int)maxlen)
ds@1952
   218
	{
ds@1952
   219
		size = maxlen;
ds@1952
   220
	}
ds@1952
   221
ds@1952
   222
	maxlen -= size;
ds@1952
   223
	width -= size;
ds@1952
   224
ds@1952
   225
	while (size--)
ds@1952
   226
	{
ds@1952
   227
		*buf++ = *string++;
ds@1952
   228
	}
ds@1952
   229
ds@1952
   230
	while ((width-- > 0) && maxlen)
ds@1952
   231
	{
ds@1952
   232
		*buf++ = ' ';
ds@1952
   233
		maxlen--;
ds@1952
   234
	}
ds@1952
   235
ds@1952
   236
	*buf_p = buf;
ds@1952
   237
}
ds@1952
   238
ds@1952
   239
void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec, int flags)
ds@1952
   240
{
ds@1952
   241
	int digits;					// non-fraction part digits
ds@1952
   242
	double tmp;					// temporary
ds@1952
   243
	char *buf = *buf_p;			// output buffer pointer
ds@1952
   244
	int val;					// temporary
ds@1952
   245
	int sign = 0;				// 0: positive, 1: negative
ds@1952
   246
	int fieldlength;			// for padding
ds@1952
   247
	int significant_digits = 0;	// number of significant digits written
ds@1952
   248
	const int MAX_SIGNIFICANT_DIGITS = 16;
ds@1952
   249
ds@1952
   250
	// default precision
ds@1952
   251
	if (prec < 0)
ds@1952
   252
	{
ds@1952
   253
		prec = 6;
ds@1952
   254
	}
ds@1952
   255
ds@1952
   256
	// get the sign
ds@1952
   257
	if (fval < 0)
ds@1952
   258
	{
ds@1952
   259
		fval = -fval;
ds@1952
   260
		sign = 1;
ds@1952
   261
	}
ds@1952
   262
ds@1952
   263
	// compute whole-part digits count
ds@1952
   264
	digits = (int)log10(fval) + 1;
ds@1952
   265
ds@1952
   266
	// Only print 0.something if 0 < fval < 1
ds@1952
   267
	if (digits < 1)
ds@1952
   268
	{
ds@1952
   269
		digits = 1;
ds@1952
   270
	}
ds@1952
   271
ds@1952
   272
	// compute the field length
ds@1952
   273
	fieldlength = digits + prec + ((prec > 0) ? 1 : 0) + sign;
ds@1952
   274
ds@1952
   275
	// minus sign BEFORE left padding if padding with zeros
ds@1952
   276
	if (sign && maxlen && (flags & ZEROPAD))
ds@1952
   277
	{
ds@1952
   278
		*buf++ = '-';
ds@1952
   279
		maxlen--;
ds@1952
   280
	}
ds@1952
   281
ds@1952
   282
	// right justify if required
ds@1952
   283
	if ((flags & LADJUST) == 0)
ds@1952
   284
	{
ds@1952
   285
		while ((fieldlength < width) && maxlen)
ds@1952
   286
		{
ds@1952
   287
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   288
			width--;
ds@1952
   289
			maxlen--;
ds@1952
   290
		}
ds@1952
   291
	}
ds@1952
   292
ds@1952
   293
	// minus sign AFTER left padding if padding with spaces
ds@1952
   294
	if (sign && maxlen && !(flags & ZEROPAD))
ds@1952
   295
	{
ds@1952
   296
		*buf++ = '-';
ds@1952
   297
		maxlen--;
ds@1952
   298
	}
ds@1952
   299
ds@1952
   300
	// write the whole part
ds@1952
   301
	tmp = pow(10.0, digits-1);
ds@1952
   302
	while ((digits--) && maxlen)
ds@1952
   303
	{
ds@1952
   304
		if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
ds@1952
   305
		{
ds@1952
   306
			*buf++ = '0';
ds@1952
   307
		}
ds@1952
   308
		else
ds@1952
   309
		{
ds@1952
   310
			val = (int)(fval / tmp);
ds@1952
   311
			*buf++ = '0' + val;
ds@1952
   312
			fval -= val * tmp;
ds@1952
   313
			tmp *= 0.1;
ds@1952
   314
		}
ds@1952
   315
		maxlen--;
ds@1952
   316
	}
ds@1952
   317
ds@1952
   318
	// write the fraction part
ds@1952
   319
	if (maxlen)
ds@1952
   320
	{
ds@1952
   321
		*buf++ = '.';
ds@1952
   322
		maxlen--;
ds@1952
   323
	}
ds@1952
   324
ds@1952
   325
	tmp = pow(10.0, prec);
ds@1952
   326
ds@1952
   327
	fval *= tmp;
ds@1952
   328
	while (prec-- && maxlen)
ds@1952
   329
	{
ds@1952
   330
		if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
ds@1952
   331
		{
ds@1952
   332
			*buf++ = '0';
ds@1952
   333
		}
ds@1952
   334
		else
ds@1952
   335
		{
ds@1952
   336
			tmp *= 0.1;
ds@1952
   337
			val = (int)(fval / tmp);
ds@1952
   338
			*buf++ = '0' + val;
ds@1952
   339
			fval -= val * tmp;
ds@1952
   340
		}
ds@1952
   341
		maxlen--;
ds@1952
   342
	}
ds@1952
   343
ds@1952
   344
	// left justify if required
ds@1952
   345
	if (flags & LADJUST)
ds@1952
   346
	{
ds@1952
   347
		while ((fieldlength < width) && maxlen)
ds@1952
   348
		{
ds@1952
   349
			// right-padding only with spaces, ZEROPAD is ignored
ds@1952
   350
			*buf++ = ' ';
ds@1952
   351
			width--;
ds@1952
   352
			maxlen--;
ds@1952
   353
		}
ds@1952
   354
	}
ds@1952
   355
ds@1952
   356
	// update parent's buffer pointer
ds@1952
   357
	*buf_p = buf;
ds@1952
   358
}
ds@1952
   359
dvander@1965
   360
void AddBinary(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
dvander@1965
   361
{
dvander@1965
   362
	char text[32];
dvander@1965
   363
	int digits;
dvander@1965
   364
	char *buf;
dvander@1965
   365
dvander@1965
   366
	digits = 0;
dvander@1965
   367
	do
dvander@1965
   368
	{
dvander@1965
   369
		if (val & 1)
dvander@1965
   370
		{
dvander@1965
   371
			text[digits++] = '1';
dvander@1965
   372
		}
dvander@1965
   373
		else
dvander@1965
   374
		{
dvander@1965
   375
			text[digits++] = '0';
dvander@1965
   376
		}
dvander@1965
   377
		val >>= 1;
dvander@1965
   378
	} while (val);
dvander@1965
   379
dvander@1965
   380
	buf = *buf_p;
dvander@1965
   381
dvander@1965
   382
	if (!(flags & LADJUST))
dvander@1965
   383
	{
dvander@1965
   384
		while (digits < width && maxlen)
dvander@1965
   385
		{
dvander@1965
   386
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
dvander@1965
   387
			width--;
dvander@1965
   388
			maxlen--;
dvander@1965
   389
		}
dvander@1965
   390
	}
dvander@1965
   391
dvander@1965
   392
	while (digits-- && maxlen)
dvander@1965
   393
	{
dvander@1965
   394
		*buf++ = text[digits];
dvander@1965
   395
		width--;
dvander@1965
   396
		maxlen--;
dvander@1965
   397
	}
dvander@1965
   398
dvander@1965
   399
	if (flags & LADJUST)
dvander@1965
   400
	{
dvander@1965
   401
		while (width-- && maxlen)
dvander@1965
   402
		{
dvander@1965
   403
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
dvander@1965
   404
			maxlen--;
dvander@1965
   405
		}
dvander@1965
   406
	}
dvander@1965
   407
dvander@1965
   408
	*buf_p = buf;
dvander@1965
   409
}
dvander@1965
   410
ds@1952
   411
void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
ds@1952
   412
{
ds@1952
   413
	char text[32];
ds@1952
   414
	int digits;
ds@1952
   415
	char *buf;
ds@1952
   416
ds@1952
   417
	digits = 0;
ds@1952
   418
	do
ds@1952
   419
	{
ds@1952
   420
		text[digits++] = '0' + val % 10;
ds@1952
   421
		val /= 10;
ds@1952
   422
	} while (val);
ds@1952
   423
ds@1952
   424
	buf = *buf_p;
ds@1952
   425
ds@1952
   426
	if (!(flags & LADJUST))
ds@1952
   427
	{
ds@1952
   428
		while (digits < width && maxlen)
ds@1952
   429
		{
ds@1952
   430
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   431
			width--;
ds@1952
   432
			maxlen--;
ds@1952
   433
		}
ds@1952
   434
	}
ds@1952
   435
ds@1952
   436
	while (digits-- && maxlen)
ds@1952
   437
	{
ds@1952
   438
		*buf++ = text[digits];
ds@1952
   439
		width--;
ds@1952
   440
		maxlen--;
ds@1952
   441
	}
ds@1952
   442
ds@1952
   443
	if (flags & LADJUST)
ds@1952
   444
	{
ds@1952
   445
		while (width-- && maxlen)
ds@1952
   446
		{
ds@1952
   447
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   448
			maxlen--;
ds@1952
   449
		}
ds@1952
   450
	}
ds@1952
   451
ds@1952
   452
	*buf_p = buf;
ds@1952
   453
}
ds@1952
   454
ds@1952
   455
void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
ds@1952
   456
{
ds@1952
   457
	char text[32];
ds@1952
   458
	int digits;
ds@1952
   459
	int signedVal;
ds@1952
   460
	char *buf;
ds@1952
   461
	unsigned int unsignedVal;
ds@1952
   462
ds@1952
   463
	digits = 0;
ds@1952
   464
	signedVal = val;
ds@1952
   465
	if (val < 0)
ds@1952
   466
	{
ds@1952
   467
		/* we want the unsigned version */
ds@1952
   468
		unsignedVal = abs(val);
ds@1952
   469
	}
ds@1952
   470
	else
ds@1952
   471
	{
ds@1952
   472
		unsignedVal = val;
ds@1952
   473
	}
ds@1952
   474
ds@1952
   475
	do
ds@1952
   476
	{
ds@1952
   477
		text[digits++] = '0' + unsignedVal % 10;
ds@1952
   478
		unsignedVal /= 10;
ds@1952
   479
	} while (unsignedVal);
ds@1952
   480
ds@1952
   481
	if (signedVal < 0)
ds@1952
   482
	{
ds@1952
   483
		text[digits++] = '-';
ds@1952
   484
	}
ds@1952
   485
ds@1952
   486
	buf = *buf_p;
ds@1952
   487
ds@1952
   488
	if (!(flags & LADJUST))
ds@1952
   489
	{
ds@1952
   490
		while ((digits < width) && maxlen)
ds@1952
   491
		{
ds@1952
   492
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   493
			width--;
ds@1952
   494
			maxlen--;
ds@1952
   495
		}
ds@1952
   496
	}
ds@1952
   497
ds@1952
   498
	while (digits-- && maxlen)
ds@1952
   499
	{
ds@1952
   500
		*buf++ = text[digits];
ds@1952
   501
		width--;
ds@1952
   502
		maxlen--;
ds@1952
   503
	}
ds@1952
   504
ds@1952
   505
	if (flags & LADJUST)
ds@1952
   506
	{
ds@1952
   507
		while (width-- && maxlen)
ds@1952
   508
		{
ds@1952
   509
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   510
			maxlen--;
ds@1952
   511
		}
ds@1952
   512
	}
ds@1952
   513
ds@1952
   514
	*buf_p = buf;
ds@1952
   515
}
ds@1952
   516
ds@1952
   517
void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
ds@1952
   518
{
ds@1952
   519
	char text[32];
ds@1952
   520
	int digits;
ds@1952
   521
	char *buf;
ds@1952
   522
	char digit;
ds@1952
   523
	int hexadjust;
ds@1952
   524
ds@1952
   525
	if (flags & UPPERDIGITS)
ds@1952
   526
	{
ds@1952
   527
		hexadjust = 'A' - '9' - 1;
ds@1952
   528
	}
ds@1952
   529
	else
ds@1952
   530
	{
ds@1952
   531
		hexadjust = 'a' - '9' - 1;
ds@1952
   532
	}
ds@1952
   533
ds@1952
   534
	digits = 0;
ds@1952
   535
	do 
ds@1952
   536
	{
ds@1952
   537
		digit = ('0' + val % 16);
ds@1952
   538
		if (digit > '9')
ds@1952
   539
		{
ds@1952
   540
			digit += hexadjust;
ds@1952
   541
		}
ds@1952
   542
ds@1952
   543
		text[digits++] = digit;
ds@1952
   544
		val /= 16;
ds@1952
   545
	} while(val);
ds@1952
   546
ds@1952
   547
	buf = *buf_p;
ds@1952
   548
ds@1952
   549
	if (!(flags & LADJUST))
ds@1952
   550
	{
ds@1952
   551
		while (digits < width && maxlen)
ds@1952
   552
		{
ds@1952
   553
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   554
			width--;
ds@1952
   555
			maxlen--;
ds@1952
   556
		}
ds@1952
   557
	}
ds@1952
   558
ds@1952
   559
	while (digits-- && maxlen)
ds@1952
   560
	{
ds@1952
   561
		*buf++ = text[digits];
ds@1952
   562
		width--;
ds@1952
   563
		maxlen--;
ds@1952
   564
	}
ds@1952
   565
ds@1952
   566
	if (flags & LADJUST)
ds@1952
   567
	{
ds@1952
   568
		while (width-- && maxlen)
ds@1952
   569
		{
ds@1952
   570
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   571
			maxlen--;
ds@1952
   572
		}
ds@1952
   573
	}
ds@1952
   574
ds@1952
   575
	*buf_p = buf;
ds@1952
   576
}
ds@1952
   577
ds@1952
   578
size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
ds@1952
   579
{
ds@1952
   580
	if (!buffer || !maxlen)
ds@1952
   581
	{
ds@1952
   582
		return 0;
ds@1952
   583
	}
ds@1952
   584
ds@1952
   585
	int arg = 0;
ds@1952
   586
	char *buf_p;
ds@1952
   587
	char ch;
ds@1952
   588
	int flags;
ds@1952
   589
	int width;
ds@1952
   590
	int prec;
ds@1952
   591
	int n;
ds@1952
   592
	char sign;
ds@1952
   593
	const char *fmt;
ds@1952
   594
	size_t llen = maxlen - 1;
ds@1952
   595
ds@1952
   596
	buf_p = buffer;
ds@1952
   597
	fmt = format;
ds@1952
   598
ds@1952
   599
	while (true)
ds@1952
   600
	{
ds@1952
   601
		// run through the format string until we hit a '%' or '\0'
ds@1952
   602
		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
ds@1952
   603
		{
ds@1952
   604
			*buf_p++ = ch;
ds@1952
   605
			llen--;
ds@1952
   606
		}
ds@1952
   607
		if ((ch == '\0') || (llen <= 0))
ds@1952
   608
		{
ds@1952
   609
			goto done;
ds@1952
   610
		}
ds@1952
   611
ds@1952
   612
		// skip over the '%'
ds@1952
   613
		fmt++;
ds@1952
   614
ds@1952
   615
		// reset formatting state
ds@1952
   616
		flags = 0;
ds@1952
   617
		width = 0;
ds@1952
   618
		prec = -1;
ds@1952
   619
		sign = '\0';
ds@1952
   620
ds@1952
   621
rflag:
ds@1952
   622
		ch = *fmt++;
ds@1952
   623
reswitch:
ds@1952
   624
		switch(ch)
ds@1952
   625
		{
ds@1952
   626
		case '-':
ds@1952
   627
			{
ds@1952
   628
				flags |= LADJUST;
ds@1952
   629
				goto rflag;
ds@1952
   630
			}
ds@1952
   631
		case '.':
ds@1952
   632
			{
ds@1952
   633
				n = 0;
ds@1952
   634
				while(is_digit((ch = *fmt++)))
ds@1952
   635
				{
ds@1952
   636
					n = 10 * n + (ch - '0');
ds@1952
   637
				}
ds@1952
   638
				prec = (n < 0) ? -1 : n;
ds@1952
   639
				goto reswitch;
ds@1952
   640
			}
ds@1952
   641
		case '0':
ds@1952
   642
			{
ds@1952
   643
				flags |= ZEROPAD;
ds@1952
   644
				goto rflag;
ds@1952
   645
			}
ds@1952
   646
		case '1':
ds@1952
   647
		case '2':
ds@1952
   648
		case '3':
ds@1952
   649
		case '4':
ds@1952
   650
		case '5':
ds@1952
   651
		case '6':
ds@1952
   652
		case '7':
ds@1952
   653
		case '8':
ds@1952
   654
		case '9':
ds@1952
   655
			{
ds@1952
   656
				n = 0;
ds@1952
   657
				do
ds@1952
   658
				{
ds@1952
   659
					n = 10 * n + (ch - '0');
ds@1952
   660
					ch = *fmt++;
ds@1952
   661
				} while(is_digit(ch));
ds@1952
   662
				width = n;
ds@1952
   663
				goto reswitch;
ds@1952
   664
			}
ds@1952
   665
		case 'c':
ds@1952
   666
			{
ds@1952
   667
				if (!llen)
ds@1952
   668
				{
ds@1952
   669
					goto done;
ds@1952
   670
				}
ds@1952
   671
				char *c = (char *)args[arg];
ds@1952
   672
				*buf_p++ = *c;
ds@1952
   673
				llen--;
ds@1952
   674
				arg++;
ds@1952
   675
				break;
ds@1952
   676
			}
dvander@1965
   677
		case 'b':
dvander@1965
   678
			{
dvander@1965
   679
				int *value = (int *)args[arg];
dvander@1965
   680
				AddBinary(&buf_p, llen, *value, width, flags);
dvander@1965
   681
				arg++;
dvander@1965
   682
				break;
dvander@1965
   683
			}
ds@1952
   684
		case 'd':
ds@1952
   685
		case 'i':
ds@1952
   686
			{
ds@1952
   687
				int *value = (int *)args[arg];
ds@1952
   688
				AddInt(&buf_p, llen, *value, width, flags);
ds@1952
   689
				arg++;
ds@1952
   690
				break;
ds@1952
   691
			}
ds@1952
   692
		case 'u':
ds@1952
   693
			{
ds@1952
   694
				unsigned int *value = (unsigned int *)args[arg];
ds@1952
   695
				AddUInt(&buf_p, llen, *value, width, flags);
ds@1952
   696
				arg++;
ds@1952
   697
				break;
ds@1952
   698
			}
ds@1952
   699
		case 'f':
ds@1952
   700
			{
ds@1952
   701
				float *value = (float *)args[arg];
ds@1952
   702
				AddFloat(&buf_p, llen, *value, width, prec, flags);
ds@1952
   703
				arg++;
ds@1952
   704
				break;
ds@1952
   705
			}
ds@1952
   706
		case 's':
ds@1952
   707
			{
ds@1952
   708
				const char *str = (const char *)args[arg];
ds@1952
   709
				AddString(&buf_p, llen, str, width, prec);
ds@1952
   710
				arg++;
ds@1952
   711
				break;
ds@1952
   712
			}
ds@1952
   713
		case 'X':
ds@1952
   714
			{
ds@1952
   715
				unsigned int *value = (unsigned int *)args[arg];
ds@1952
   716
				flags |= UPPERDIGITS;
ds@1952
   717
				AddHex(&buf_p, llen, *value, width, flags);
ds@1952
   718
				arg++;
ds@1952
   719
				break;
ds@1952
   720
			}
ds@1952
   721
		case 'x':
ds@1952
   722
			{
ds@1952
   723
				unsigned int *value = (unsigned int *)args[arg];
ds@1952
   724
				AddHex(&buf_p, llen, *value, width, flags);
ds@1952
   725
				arg++;
ds@1952
   726
				break;
ds@1952
   727
			}
ds@1952
   728
		case '%':
ds@1952
   729
			{
ds@1952
   730
				if (!llen)
ds@1952
   731
				{
ds@1952
   732
					goto done;
ds@1952
   733
				}
ds@1952
   734
				*buf_p++ = ch;
ds@1952
   735
				llen--;
ds@1952
   736
				break;
ds@1952
   737
			}
ds@1952
   738
		case '\0':
ds@1952
   739
			{
ds@1952
   740
				if (!llen)
ds@1952
   741
				{
ds@1952
   742
					goto done;
ds@1952
   743
				}
ds@1952
   744
				*buf_p++ = '%';
ds@1952
   745
				llen--;
ds@1952
   746
				goto done;
ds@1952
   747
			}
ds@1952
   748
		default:
ds@1952
   749
			{
ds@1952
   750
				if (!llen)
ds@1952
   751
				{
ds@1952
   752
					goto done;
ds@1952
   753
				}
ds@1952
   754
				*buf_p++ = ch;
ds@1952
   755
				llen--;
ds@1952
   756
				break;
ds@1952
   757
			}
ds@1952
   758
		}
ds@1952
   759
	}
ds@1952
   760
ds@1952
   761
done:
ds@1952
   762
	*buf_p = '\0';
ds@1952
   763
ds@1952
   764
	return (maxlen - llen - 1);
ds@1952
   765
}
ds@1952
   766
ds@1952
   767
size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
ds@1952
   768
{
ds@1952
   769
	if (!buffer || !maxlen)
ds@1952
   770
	{
ds@1952
   771
		return 0;
ds@1952
   772
	}
ds@1952
   773
ds@1952
   774
	int arg;
ds@1952
   775
	int args = params[0];
ds@1952
   776
	char *buf_p;
ds@1952
   777
	char ch;
ds@1952
   778
	int flags;
ds@1952
   779
	int width;
ds@1952
   780
	int prec;
ds@1952
   781
	int n;
ds@1952
   782
	char sign;
ds@1952
   783
	const char *fmt;
ds@1952
   784
	size_t llen = maxlen - 1;
ds@1952
   785
ds@1952
   786
	buf_p = buffer;
ds@1952
   787
	arg = *param;
ds@1952
   788
	fmt = format;
ds@1952
   789
ds@1952
   790
	while (true)
ds@1952
   791
	{
ds@1952
   792
		// run through the format string until we hit a '%' or '\0'
ds@1952
   793
		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
ds@1952
   794
		{
ds@1952
   795
			*buf_p++ = ch;
ds@1952
   796
			llen--;
ds@1952
   797
		}
ds@1952
   798
		if ((ch == '\0') || (llen <= 0))
ds@1952
   799
		{
ds@1952
   800
			goto done;
ds@1952
   801
		}
ds@1952
   802
ds@1952
   803
		// skip over the '%'
ds@1952
   804
		fmt++;
ds@1952
   805
ds@1952
   806
		// reset formatting state
ds@1952
   807
		flags = 0;
ds@1952
   808
		width = 0;
ds@1952
   809
		prec = -1;
ds@1952
   810
		sign = '\0';
ds@1952
   811
ds@1952
   812
rflag:
ds@1952
   813
		ch = *fmt++;
ds@1952
   814
reswitch:
ds@1952
   815
		switch(ch)
ds@1952
   816
		{
ds@1952
   817
		case '-':
ds@1952
   818
			{
ds@1952
   819
				flags |= LADJUST;
ds@1952
   820
				goto rflag;
ds@1952
   821
			}
ds@1952
   822
		case '.':
ds@1952
   823
			{
ds@1952
   824
				n = 0;
ds@1952
   825
				while(is_digit((ch = *fmt++)))
ds@1952
   826
				{
ds@1952
   827
					n = 10 * n + (ch - '0');
ds@1952
   828
				}
ds@1952
   829
				prec = (n < 0) ? -1 : n;
ds@1952
   830
				goto reswitch;
ds@1952
   831
			}
ds@1952
   832
		case '0':
ds@1952
   833
			{
ds@1952
   834
				flags |= ZEROPAD;
ds@1952
   835
				goto rflag;
ds@1952
   836
			}
ds@1952
   837
		case '1':
ds@1952
   838
		case '2':
ds@1952
   839
		case '3':
ds@1952
   840
		case '4':
ds@1952
   841
		case '5':
ds@1952
   842
		case '6':
ds@1952
   843
		case '7':
ds@1952
   844
		case '8':
ds@1952
   845
		case '9':
ds@1952
   846
			{
ds@1952
   847
				n = 0;
ds@1952
   848
				do
ds@1952
   849
				{
ds@1952
   850
					n = 10 * n + (ch - '0');
ds@1952
   851
					ch = *fmt++;
ds@1952
   852
				} while(is_digit(ch));
ds@1952
   853
				width = n;
ds@1952
   854
				goto reswitch;
ds@1952
   855
			}
ds@1952
   856
		case 'c':
ds@1952
   857
			{
ds@1952
   858
				CHECK_ARGS(0);
ds@1952
   859
				if (!llen)
ds@1952
   860
				{
ds@1952
   861
					goto done;
ds@1952
   862
				}
ds@1952
   863
				char *c;
ds@1952
   864
				pCtx->LocalToString(params[arg], &c);
ds@1952
   865
				*buf_p++ = *c;
ds@1952
   866
				llen--;
ds@1952
   867
				arg++;
ds@1952
   868
				break;
ds@1952
   869
			}
dvander@1965
   870
		case 'b':
dvander@1965
   871
			{
dvander@1965
   872
				CHECK_ARGS(0);
dvander@1965
   873
				cell_t *value;
dvander@1965
   874
				pCtx->LocalToPhysAddr(params[arg], &value);
dvander@1965
   875
				AddBinary(&buf_p, llen, *value, width, flags);
dvander@1965
   876
				arg++;
dvander@1965
   877
				break;
dvander@1965
   878
			}
ds@1952
   879
		case 'd':
ds@1952
   880
		case 'i':
ds@1952
   881
			{
ds@1952
   882
				CHECK_ARGS(0);
ds@1952
   883
				cell_t *value;
ds@1952
   884
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   885
				AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
ds@1952
   886
				arg++;
ds@1952
   887
				break;
ds@1952
   888
			}
ds@1952
   889
		case 'u':
ds@1952
   890
			{
ds@1952
   891
				CHECK_ARGS(0);
ds@1952
   892
				cell_t *value;
ds@1952
   893
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   894
				AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
ds@1952
   895
				arg++;
ds@1952
   896
				break;
ds@1952
   897
			}
ds@1952
   898
		case 'f':
ds@1952
   899
			{
ds@1952
   900
				CHECK_ARGS(0);
ds@1952
   901
				cell_t *value;
ds@1952
   902
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   903
				AddFloat(&buf_p, llen, sp_ctof(*value), width, prec, flags);
ds@1952
   904
				arg++;
ds@1952
   905
				break;
ds@1952
   906
			}
ds@1952
   907
		case 'L':
ds@1952
   908
			{
ds@1952
   909
				CHECK_ARGS(0);
ds@1952
   910
				cell_t *value;
ds@1952
   911
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   912
				char buffer[255];
ds@1952
   913
				if (*value)
ds@1952
   914
				{
ds@1952
   915
					CPlayer *player = g_Players.GetPlayerByIndex(*value);
ds@1952
   916
					if (!player || !player->IsConnected())
ds@1952
   917
					{
ds@1952
   918
						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
ds@1952
   919
					}
ds@1952
   920
					const char *auth = player->GetAuthString();
ds@1952
   921
					if (!auth || auth[0] == '\0')
ds@1952
   922
					{
ds@1952
   923
						auth = "STEAM_ID_PENDING";
ds@1952
   924
					}
ds@1952
   925
					int userid = engine->GetPlayerUserId(player->GetEdict());
ds@1952
   926
					UTIL_Format(buffer, 
ds@1952
   927
						sizeof(buffer), 
ds@1952
   928
						"%s<%d><%s><>", 
ds@1952
   929
						player->GetName(),
ds@1952
   930
						userid,
ds@1952
   931
						auth);
ds@1952
   932
				}
ds@1952
   933
				else
ds@1952
   934
				{
ds@1952
   935
					UTIL_Format(buffer,
ds@1952
   936
						sizeof(buffer),
ds@1952
   937
						"Console<0><Console><Console>");
ds@1952
   938
				}
ds@1952
   939
				AddString(&buf_p, llen, buffer, width, prec);
ds@1952
   940
				arg++;
ds@1952
   941
				break;
ds@1952
   942
			}
ds@1952
   943
		case 'N':
ds@1952
   944
			{
ds@1952
   945
				CHECK_ARGS(0);
ds@1952
   946
				cell_t *value;
ds@1952
   947
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   948
ds@1952
   949
				const char *name = "Console";
ds@1952
   950
				if (*value)
ds@1952
   951
				{
ds@1952
   952
					CPlayer *player = g_Players.GetPlayerByIndex(*value);
ds@1952
   953
					if (!player || !player->IsConnected())
ds@1952
   954
					{
ds@1952
   955
						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
ds@1952
   956
					}
ds@1952
   957
					name = player->GetName();
ds@1952
   958
				}
ds@1952
   959
				AddString(&buf_p, llen, name, width, prec);
ds@1952
   960
				arg++;
ds@1952
   961
				break;
ds@1952
   962
			}
ds@1952
   963
		case 's':
ds@1952
   964
			{
ds@1952
   965
				CHECK_ARGS(0);
ds@1952
   966
				char *str;
ds@1952
   967
				int err;
ds@1952
   968
				if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
ds@1952
   969
				{
ds@1952
   970
					pCtx->ThrowNativeErrorEx(err, "Could not deference string");
ds@1952
   971
					return 0;
ds@1952
   972
				}
ds@1952
   973
				AddString(&buf_p, llen, str, width, prec);
ds@1952
   974
				arg++;
ds@1952
   975
				break;
ds@1952
   976
			}
ds@1952
   977
		case 'T':
ds@1952
   978
			{
ds@1952
   979
				CHECK_ARGS(1);
ds@1952
   980
				char *key;
ds@1952
   981
				bool error;
ds@1952
   982
				size_t res;
ds@1952
   983
				cell_t *target;
ds@1952
   984
				pCtx->LocalToString(params[arg++], &key);
ds@1952
   985
				pCtx->LocalToPhysAddr(params[arg++], &target);
ds@1952
   986
				res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
ds@1952
   987
				if (error)
ds@1952
   988
				{
ds@1952
   989
					return 0;
ds@1952
   990
				}
ds@1952
   991
				buf_p += res;
ds@1952
   992
				llen -= res;
ds@1952
   993
				break;
ds@1952
   994
			}
ds@1952
   995
		case 't':
ds@1952
   996
			{
ds@1952
   997
				CHECK_ARGS(0);
ds@1952
   998
				char *key;
ds@1952
   999
				bool error;
ds@1952
  1000
				size_t res;
ds@1952
  1001
				cell_t target = g_SourceMod.GetGlobalTarget();
ds@1952
  1002
				pCtx->LocalToString(params[arg++], &key);
ds@1952
  1003
				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
ds@1952
  1004
				if (error)
ds@1952
  1005
				{
ds@1952
  1006
					return 0;
ds@1952
  1007
				}
ds@1952
  1008
				buf_p += res;
ds@1952
  1009
				llen -= res;
ds@1952
  1010
				break;
ds@1952
  1011
			}
ds@1952
  1012
		case 'X':
ds@1952
  1013
			{
ds@1952
  1014
				CHECK_ARGS(0);
ds@1952
  1015
				cell_t *value;
ds@1952
  1016
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
  1017
				flags |= UPPERDIGITS;
ds@1952
  1018
				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
ds@1952
  1019
				arg++;
ds@1952
  1020
				break;
ds@1952
  1021
			}
ds@1952
  1022
		case 'x':
ds@1952
  1023
			{
ds@1952
  1024
				CHECK_ARGS(0);
ds@1952
  1025
				cell_t *value;
ds@1952
  1026
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
  1027
				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
ds@1952
  1028
				arg++;
ds@1952
  1029
				break;
ds@1952
  1030
			}
ds@1952
  1031
		case '%':
ds@1952
  1032
			{
ds@1952
  1033
				if (!llen)
ds@1952
  1034
				{
ds@1952
  1035
					goto done;
ds@1952
  1036
				}
ds@1952
  1037
				*buf_p++ = ch;
ds@1952
  1038
				llen--;
ds@1952
  1039
				break;
ds@1952
  1040
			}
ds@1952
  1041
		case '\0':
ds@1952
  1042
			{
ds@1952
  1043
				if (!llen)
ds@1952
  1044
				{
ds@1952
  1045
					goto done;
ds@1952
  1046
				}
ds@1952
  1047
				*buf_p++ = '%';
ds@1952
  1048
				llen--;
ds@1952
  1049
				goto done;
ds@1952
  1050
			}
ds@1952
  1051
		default:
ds@1952
  1052
			{
ds@1952
  1053
				if (!llen)
ds@1952
  1054
				{
ds@1952
  1055
					goto done;
ds@1952
  1056
				}
ds@1952
  1057
				*buf_p++ = ch;
ds@1952
  1058
				llen--;
ds@1952
  1059
				break;
ds@1952
  1060
			}
ds@1952
  1061
		}
ds@1952
  1062
	}
ds@1952
  1063
ds@1952
  1064
done:
ds@1952
  1065
	*buf_p = '\0';
ds@1952
  1066
	*param = arg;
ds@1952
  1067
	return (maxlen - llen - 1);
ds@1952
  1068
}
ds@1952
  1069
ds@1952
  1070
const char *stristr(const char *str, const char *substr)
ds@1952
  1071
{
ds@1952
  1072
	if (!*substr)
ds@1952
  1073
	{
ds@1952
  1074
		return ((char *)str);
ds@1952
  1075
	}
ds@1952
  1076
ds@1952
  1077
	char *needle = (char *)substr;
ds@1952
  1078
	char *prevloc = (char *)str;
ds@1952
  1079
	char *haystack = (char *)str;
ds@1952
  1080
ds@1952
  1081
	while (*haystack)
ds@1952
  1082
	{
ds@1952
  1083
		if (tolower(*haystack) == tolower(*needle))
ds@1952
  1084
		{
ds@1952
  1085
			haystack++;
ds@1952
  1086
			if (!*++needle)
ds@1952
  1087
			{
ds@1952
  1088
				return prevloc;
ds@1952
  1089
			}
ds@1952
  1090
		}
ds@1952
  1091
		else
ds@1952
  1092
		{
ds@1952
  1093
			haystack = ++prevloc;
ds@1952
  1094
			needle = (char *)substr;
ds@1952
  1095
		}
ds@1952
  1096
	}
ds@1952
  1097
ds@1952
  1098
	return NULL;
ds@1952
  1099
}
ds@1952
  1100
ds@1952
  1101
unsigned int strncopy(char *dest, const char *src, size_t count)
ds@1952
  1102
{
ds@1952
  1103
	if (!count)
ds@1952
  1104
	{
ds@1952
  1105
		return 0;
ds@1952
  1106
	}
ds@1952
  1107
ds@1952
  1108
	char *start = dest;
ds@1952
  1109
	while ((*src) && (--count))
ds@1952
  1110
	{
ds@1952
  1111
		*dest++ = *src++;
ds@1952
  1112
	}
ds@1952
  1113
	*dest = '\0';
ds@1952
  1114
ds@1952
  1115
	return (dest - start);
ds@1952
  1116
}
ds@1952
  1117
ds@1952
  1118
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
ds@1952
  1119
{
ds@1952
  1120
	va_list ap;
ds@1952
  1121
	va_start(ap, fmt);
ds@1952
  1122
	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
ds@1952
  1123
	va_end(ap);
ds@1952
  1124
ds@1952
  1125
	if (len >= maxlength)
ds@1952
  1126
	{
ds@1952
  1127
		buffer[maxlength - 1] = '\0';
ds@1952
  1128
		return (maxlength - 1);
ds@1952
  1129
	}
ds@1952
  1130
	else
ds@1952
  1131
	{
ds@1952
  1132
		return len;
ds@1952
  1133
	}
ds@1952
  1134
}
ds@1952
  1135
ds@1952
  1136
size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
ds@1952
  1137
{
ds@1952
  1138
	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
ds@1952
  1139
ds@1952
  1140
	if (len >= maxlength)
ds@1952
  1141
	{
ds@1952
  1142
		buffer[maxlength - 1] = '\0';
ds@1952
  1143
		return (maxlength - 1);
ds@1952
  1144
	}
ds@1952
  1145
	else
ds@1952
  1146
	{
ds@1952
  1147
		return len;
ds@1952
  1148
	}
ds@1952
  1149
}
ds@1952
  1150
ds@1952
  1151
char *sm_strdup(const char *str)
ds@1952
  1152
{
ds@1952
  1153
	char *ptr = new char[strlen(str)+1];
ds@1952
  1154
	strcpy(ptr, str);
ds@1952
  1155
	return ptr;
ds@1952
  1156
}
ds@1952
  1157
ds@1952
  1158
unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
ds@1952
  1159
{
ds@1952
  1160
	size_t searchLen = strlen(search);
ds@1952
  1161
	size_t replaceLen = strlen(replace);
ds@1952
  1162
ds@1952
  1163
	char *ptr = subject;
ds@1952
  1164
	unsigned int total = 0;
ds@1952
  1165
	while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
ds@1952
  1166
	{
ds@1952
  1167
		total++;
ds@1952
  1168
		if (*ptr == '\0')
ds@1952
  1169
		{
ds@1952
  1170
			break;
ds@1952
  1171
		}
ds@1952
  1172
	}
ds@1952
  1173
ds@1952
  1174
	return total;
ds@1952
  1175
}
ds@1952
  1176
ds@1952
  1177
/**
ds@1952
  1178
 * NOTE: Do not edit this for the love of god unless you have 
ds@1952
  1179
 * read the test cases and understand the code behind each one.
ds@1952
  1180
 * While I don't guarantee there aren't mistakes, I do guarantee
ds@1952
  1181
 * that plugins will end up relying on tiny idiosyncracies of this
ds@1952
  1182
 * function, just like they did with AMX Mod X.
ds@1952
  1183
 *
ds@1952
  1184
 * There are explicitly more cases than the AMX Mod X version because
ds@1952
  1185
 * we're not doing a blind copy.  Each case is specifically optimized
ds@1952
  1186
 * for what needs to be done.  Even better, we don't have to error on
ds@1952
  1187
 * bad buffer sizes.  Instead, this function will smartly cut off the 
ds@1952
  1188
 * string in a way that pushes old data out.
ds@1952
  1189
 */
ds@1952
  1190
char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
ds@1952
  1191
{
ds@1952
  1192
	char *ptr = subject;
ds@1952
  1193
	size_t browsed = 0;
ds@1952
  1194
	size_t textLen = strlen(subject);
ds@1952
  1195
ds@1952
  1196
	/* It's not possible to search or replace */
ds@1952
  1197
	if (searchLen > textLen)
ds@1952
  1198
	{
ds@1952
  1199
		return NULL;
ds@1952
  1200
	}
ds@1952
  1201
ds@1952
  1202
	/* Handle the case of one byte replacement.
ds@1952
  1203
	 * It's only valid in one case.
ds@1952
  1204
	 */
ds@1952
  1205
	if (maxLen == 1)
ds@1952
  1206
	{
ds@1952
  1207
		/* If the search matches and the replace length is 0, 
ds@1952
  1208
		 * we can just terminate the string and be done.
ds@1952
  1209
		 */
ds@1952
  1210
		if (strcmp(subject, search) == 0 && replaceLen == 0)
ds@1952
  1211
		{
ds@1952
  1212
			*subject = '\0';
ds@1952
  1213
			return subject;
ds@1952
  1214
		}
ds@1952
  1215
		else
ds@1952
  1216
		{
ds@1952
  1217
			return NULL;
ds@1952
  1218
		}
ds@1952
  1219
	}
ds@1952
  1220
ds@1952
  1221
	/* Subtract one off the maxlength so we can include the null terminator */
ds@1952
  1222
	maxLen--;
ds@1952
  1223
ds@1952
  1224
	while (*ptr != '\0' && (browsed <= textLen - searchLen))
ds@1952
  1225
	{
ds@1952
  1226
		/* See if we get a comparison */
ds@1952
  1227
		if (strncmp(ptr, search, searchLen) == 0)
ds@1952
  1228
		{
ds@1952
  1229
			if (replaceLen > searchLen)
ds@1952
  1230
			{
ds@1952
  1231
				/* First, see if we have enough space to do this operation */
ds@1952
  1232
				if (maxLen - textLen < replaceLen - searchLen)
ds@1952
  1233
				{
ds@1952
  1234
					/* First, see if the replacement length goes out of bounds. */
ds@1952
  1235
					if (browsed + replaceLen >= maxLen)
ds@1952
  1236
					{
ds@1952
  1237
						/* EXAMPLE CASE:
ds@1952
  1238
						 * Subject: AABBBCCC
ds@1952
  1239
						 * Buffer : 12 bytes
ds@1952
  1240
						 * Search : BBB
ds@1952
  1241
						 * Replace: DDDDDDDDDD
ds@1952
  1242
						 * OUTPUT : AADDDDDDDDD
ds@1952
  1243
						 * POSITION:           ^
ds@1952
  1244
						 */
ds@1952
  1245
						/* If it does, we'll just bound the length and do a strcpy. */
ds@1952
  1246
						replaceLen = maxLen - browsed;
ds@1952
  1247
						/* Note, we add one to the final result for the null terminator */
ds@1952
  1248
						strncopy(ptr, replace, replaceLen+1);
ds@1952
  1249
					}
ds@1952
  1250
					else
ds@1952
  1251
					{
ds@1952
  1252
						/* EXAMPLE CASE:
ds@1952
  1253
						 * Subject: AABBBCCC
ds@1952
  1254
						 * Buffer : 12 bytes
ds@1952
  1255
						 * Search : BBB
ds@1952
  1256
						 * Replace: DDDDDDD
ds@1952
  1257
						 * OUTPUT : AADDDDDDDCC
ds@1952
  1258
						 * POSITION:         ^
ds@1952
  1259
						 */
ds@1952
  1260
						/* We're going to have some bytes left over... */
ds@1952
  1261
						size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
ds@1952
  1262
						size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
ds@1952
  1263
						char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
ds@1952
  1264
						char *moveTo = ptr + replaceLen;
ds@1952
  1265
ds@1952
  1266
						/* First, move our old data out of the way. */
ds@1952
  1267
						memmove(moveTo, moveFrom, realBytesToCopy);
ds@1952
  1268
ds@1952
  1269
						/* Now, do our replacement. */
ds@1952
  1270
						memcpy(ptr, replace, replaceLen);
ds@1952
  1271
					}
ds@1952
  1272
				}
ds@1952
  1273
				else
ds@1952
  1274
				{
ds@1952
  1275
					/* EXAMPLE CASE:
ds@1952
  1276
					 * Subject: AABBBCCC
ds@1952
  1277
					 * Buffer : 12 bytes
ds@1952
  1278
					 * Search : BBB
ds@1952
  1279
					 * Replace: DDDD
ds@1952
  1280
					 * OUTPUT : AADDDDCCC
ds@1952
  1281
					 * POSITION:      ^
ds@1952
  1282
					 */
ds@1952
  1283
					/* Yes, we have enough space.  Do a normal move operation. */
ds@1952
  1284
					char *moveFrom = ptr + searchLen;
ds@1952
  1285
					char *moveTo = ptr + replaceLen;
ds@1952
  1286
ds@1952
  1287
					/* First move our old data out of the way. */
ds@1952
  1288
					size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
ds@1952
  1289
					memmove(moveTo, moveFrom, bytesToCopy);
ds@1952
  1290
ds@1952
  1291
					/* Now do our replacement. */
ds@1952
  1292
					memcpy(ptr, replace, replaceLen);
ds@1952
  1293
				}
ds@1952
  1294
			}
ds@1952
  1295
			else if (replaceLen < searchLen)
ds@1952
  1296
			{
ds@1952
  1297
				/* EXAMPLE CASE:
ds@1952
  1298
				 * Subject: AABBBCCC
ds@1952
  1299
				 * Buffer : 12 bytes
ds@1952
  1300
				 * Search : BBB
ds@1952
  1301
				 * Replace: D
ds@1952
  1302
				 * OUTPUT : AADCCC
ds@1952
  1303
				 * POSOTION:   ^
ds@1952
  1304
				 */
ds@1952
  1305
				/* If the replacement does not grow the string length, we do not
ds@1952
  1306
				 * need to do any fancy checking at all.  Yay!
ds@1952
  1307
				 */
ds@1952
  1308
				char *moveFrom = ptr + searchLen;		/* Start after the search pointer */
ds@1952
  1309
				char *moveTo = ptr + replaceLen;		/* Copy to where the replacement ends */
ds@1952
  1310
ds@1952
  1311
				/* Copy our replacement in, if any */
ds@1952
  1312
				if (replaceLen)
ds@1952
  1313
				{
ds@1952
  1314
					memcpy(ptr, replace, replaceLen);
ds@1952
  1315
				}
ds@1952
  1316
ds@1952
  1317
				/* Figure out how many bytes to move down, including null terminator */
ds@1952
  1318
				size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
ds@1952
  1319
ds@1952
  1320
				/* Move the rest of the string down */
ds@1952
  1321
				memmove(moveTo, moveFrom, bytesToCopy);
ds@1952
  1322
			}
ds@1952
  1323
			else
ds@1952
  1324
			{
ds@1952
  1325
				/* EXAMPLE CASE:
ds@1952
  1326
				 * Subject: AABBBCCC
ds@1952
  1327
				 * Buffer : 12 bytes
ds@1952
  1328
				 * Search : BBB
ds@1952
  1329
				 * Replace: DDD
ds@1952
  1330
				 * OUTPUT : AADDDCCC
ds@1952
  1331
				 * POSITION:     ^
ds@1952
  1332
				 */
ds@1952
  1333
				/* We don't have to move anything around, just do a straight copy */
ds@1952
  1334
				memcpy(ptr, replace, replaceLen);
ds@1952
  1335
			}
ds@1952
  1336
ds@1952
  1337
			return ptr + replaceLen;
ds@1952
  1338
		}
ds@1952
  1339
		ptr++;
ds@1952
  1340
		browsed++;
ds@1952
  1341
	}
ds@1952
  1342
ds@1952
  1343
	return NULL;
ds@1952
  1344
}
ds@1952
  1345
ds@1952
  1346
char *UTIL_TrimWhitespace(char *str, size_t &len)
ds@1952
  1347
{
ds@1952
  1348
	char *end = str + len - 1;
ds@1952
  1349
ds@1952
  1350
	if (!len)
ds@1952
  1351
	{
ds@1952
  1352
		return str;
ds@1952
  1353
	}
ds@1952
  1354
ds@1952
  1355
	/* Iterate backwards through string until we reach first non-whitespace char */
ds@1952
  1356
	while (end >= str && textparsers->IsWhitespace(end))
ds@1952
  1357
	{
ds@1952
  1358
		end--;
ds@1952
  1359
		len--;
ds@1952
  1360
	}
ds@1952
  1361
ds@1952
  1362
	/* Replace first whitespace char (at the end) with null terminator.
ds@1952
  1363
	 * If there is none, we're just replacing the null terminator. 
ds@1952
  1364
	 */
ds@1952
  1365
	*(end + 1) = '\0';
ds@1952
  1366
ds@1952
  1367
	while (*str != '\0' && textparsers->IsWhitespace(str))
ds@1952
  1368
	{
ds@1952
  1369
		str++;
ds@1952
  1370
		len--;
ds@1952
  1371
	}
ds@1952
  1372
ds@1952
  1373
	return str;
ds@1952
  1374
}
ds@1952
  1375