core/sm_stringutil.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Mar 30 07:00:22 2008 +0000 (2008-03-30)
changeset 1952 b7aa2b626807
child 1965 63d5a5f6935f
permissions -rw-r--r--
Spring Cleaning, Part Ichi (1)
Various minor things done to project files
Updated sample extension project file and updated makefile to the new unified version (more changes likely on the way)
Updated regex project file and makefile
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
ds@1952
   360
void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
ds@1952
   361
{
ds@1952
   362
	char text[32];
ds@1952
   363
	int digits;
ds@1952
   364
	char *buf;
ds@1952
   365
ds@1952
   366
	digits = 0;
ds@1952
   367
	do
ds@1952
   368
	{
ds@1952
   369
		text[digits++] = '0' + val % 10;
ds@1952
   370
		val /= 10;
ds@1952
   371
	} while (val);
ds@1952
   372
ds@1952
   373
	buf = *buf_p;
ds@1952
   374
ds@1952
   375
	if (!(flags & LADJUST))
ds@1952
   376
	{
ds@1952
   377
		while (digits < width && maxlen)
ds@1952
   378
		{
ds@1952
   379
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   380
			width--;
ds@1952
   381
			maxlen--;
ds@1952
   382
		}
ds@1952
   383
	}
ds@1952
   384
ds@1952
   385
	while (digits-- && maxlen)
ds@1952
   386
	{
ds@1952
   387
		*buf++ = text[digits];
ds@1952
   388
		width--;
ds@1952
   389
		maxlen--;
ds@1952
   390
	}
ds@1952
   391
ds@1952
   392
	if (flags & LADJUST)
ds@1952
   393
	{
ds@1952
   394
		while (width-- && maxlen)
ds@1952
   395
		{
ds@1952
   396
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   397
			maxlen--;
ds@1952
   398
		}
ds@1952
   399
	}
ds@1952
   400
ds@1952
   401
	*buf_p = buf;
ds@1952
   402
}
ds@1952
   403
ds@1952
   404
void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
ds@1952
   405
{
ds@1952
   406
	char text[32];
ds@1952
   407
	int digits;
ds@1952
   408
	int signedVal;
ds@1952
   409
	char *buf;
ds@1952
   410
	unsigned int unsignedVal;
ds@1952
   411
ds@1952
   412
	digits = 0;
ds@1952
   413
	signedVal = val;
ds@1952
   414
	if (val < 0)
ds@1952
   415
	{
ds@1952
   416
		/* we want the unsigned version */
ds@1952
   417
		unsignedVal = abs(val);
ds@1952
   418
	}
ds@1952
   419
	else
ds@1952
   420
	{
ds@1952
   421
		unsignedVal = val;
ds@1952
   422
	}
ds@1952
   423
ds@1952
   424
	do
ds@1952
   425
	{
ds@1952
   426
		text[digits++] = '0' + unsignedVal % 10;
ds@1952
   427
		unsignedVal /= 10;
ds@1952
   428
	} while (unsignedVal);
ds@1952
   429
ds@1952
   430
	if (signedVal < 0)
ds@1952
   431
	{
ds@1952
   432
		text[digits++] = '-';
ds@1952
   433
	}
ds@1952
   434
ds@1952
   435
	buf = *buf_p;
ds@1952
   436
ds@1952
   437
	if (!(flags & LADJUST))
ds@1952
   438
	{
ds@1952
   439
		while ((digits < width) && maxlen)
ds@1952
   440
		{
ds@1952
   441
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   442
			width--;
ds@1952
   443
			maxlen--;
ds@1952
   444
		}
ds@1952
   445
	}
ds@1952
   446
ds@1952
   447
	while (digits-- && maxlen)
ds@1952
   448
	{
ds@1952
   449
		*buf++ = text[digits];
ds@1952
   450
		width--;
ds@1952
   451
		maxlen--;
ds@1952
   452
	}
ds@1952
   453
ds@1952
   454
	if (flags & LADJUST)
ds@1952
   455
	{
ds@1952
   456
		while (width-- && maxlen)
ds@1952
   457
		{
ds@1952
   458
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   459
			maxlen--;
ds@1952
   460
		}
ds@1952
   461
	}
ds@1952
   462
ds@1952
   463
	*buf_p = buf;
ds@1952
   464
}
ds@1952
   465
ds@1952
   466
void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
ds@1952
   467
{
ds@1952
   468
	char text[32];
ds@1952
   469
	int digits;
ds@1952
   470
	char *buf;
ds@1952
   471
	char digit;
ds@1952
   472
	int hexadjust;
ds@1952
   473
ds@1952
   474
	if (flags & UPPERDIGITS)
ds@1952
   475
	{
ds@1952
   476
		hexadjust = 'A' - '9' - 1;
ds@1952
   477
	}
ds@1952
   478
	else
ds@1952
   479
	{
ds@1952
   480
		hexadjust = 'a' - '9' - 1;
ds@1952
   481
	}
ds@1952
   482
ds@1952
   483
	digits = 0;
ds@1952
   484
	do 
ds@1952
   485
	{
ds@1952
   486
		digit = ('0' + val % 16);
ds@1952
   487
		if (digit > '9')
ds@1952
   488
		{
ds@1952
   489
			digit += hexadjust;
ds@1952
   490
		}
ds@1952
   491
ds@1952
   492
		text[digits++] = digit;
ds@1952
   493
		val /= 16;
ds@1952
   494
	} while(val);
ds@1952
   495
ds@1952
   496
	buf = *buf_p;
ds@1952
   497
ds@1952
   498
	if (!(flags & LADJUST))
ds@1952
   499
	{
ds@1952
   500
		while (digits < width && maxlen)
ds@1952
   501
		{
ds@1952
   502
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   503
			width--;
ds@1952
   504
			maxlen--;
ds@1952
   505
		}
ds@1952
   506
	}
ds@1952
   507
ds@1952
   508
	while (digits-- && maxlen)
ds@1952
   509
	{
ds@1952
   510
		*buf++ = text[digits];
ds@1952
   511
		width--;
ds@1952
   512
		maxlen--;
ds@1952
   513
	}
ds@1952
   514
ds@1952
   515
	if (flags & LADJUST)
ds@1952
   516
	{
ds@1952
   517
		while (width-- && maxlen)
ds@1952
   518
		{
ds@1952
   519
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   520
			maxlen--;
ds@1952
   521
		}
ds@1952
   522
	}
ds@1952
   523
ds@1952
   524
	*buf_p = buf;
ds@1952
   525
}
ds@1952
   526
ds@1952
   527
size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
ds@1952
   528
{
ds@1952
   529
	if (!buffer || !maxlen)
ds@1952
   530
	{
ds@1952
   531
		return 0;
ds@1952
   532
	}
ds@1952
   533
ds@1952
   534
	int arg = 0;
ds@1952
   535
	char *buf_p;
ds@1952
   536
	char ch;
ds@1952
   537
	int flags;
ds@1952
   538
	int width;
ds@1952
   539
	int prec;
ds@1952
   540
	int n;
ds@1952
   541
	char sign;
ds@1952
   542
	const char *fmt;
ds@1952
   543
	size_t llen = maxlen - 1;
ds@1952
   544
ds@1952
   545
	buf_p = buffer;
ds@1952
   546
	fmt = format;
ds@1952
   547
ds@1952
   548
	while (true)
ds@1952
   549
	{
ds@1952
   550
		// run through the format string until we hit a '%' or '\0'
ds@1952
   551
		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
ds@1952
   552
		{
ds@1952
   553
			*buf_p++ = ch;
ds@1952
   554
			llen--;
ds@1952
   555
		}
ds@1952
   556
		if ((ch == '\0') || (llen <= 0))
ds@1952
   557
		{
ds@1952
   558
			goto done;
ds@1952
   559
		}
ds@1952
   560
ds@1952
   561
		// skip over the '%'
ds@1952
   562
		fmt++;
ds@1952
   563
ds@1952
   564
		// reset formatting state
ds@1952
   565
		flags = 0;
ds@1952
   566
		width = 0;
ds@1952
   567
		prec = -1;
ds@1952
   568
		sign = '\0';
ds@1952
   569
ds@1952
   570
rflag:
ds@1952
   571
		ch = *fmt++;
ds@1952
   572
reswitch:
ds@1952
   573
		switch(ch)
ds@1952
   574
		{
ds@1952
   575
		case '-':
ds@1952
   576
			{
ds@1952
   577
				flags |= LADJUST;
ds@1952
   578
				goto rflag;
ds@1952
   579
			}
ds@1952
   580
		case '.':
ds@1952
   581
			{
ds@1952
   582
				n = 0;
ds@1952
   583
				while(is_digit((ch = *fmt++)))
ds@1952
   584
				{
ds@1952
   585
					n = 10 * n + (ch - '0');
ds@1952
   586
				}
ds@1952
   587
				prec = (n < 0) ? -1 : n;
ds@1952
   588
				goto reswitch;
ds@1952
   589
			}
ds@1952
   590
		case '0':
ds@1952
   591
			{
ds@1952
   592
				flags |= ZEROPAD;
ds@1952
   593
				goto rflag;
ds@1952
   594
			}
ds@1952
   595
		case '1':
ds@1952
   596
		case '2':
ds@1952
   597
		case '3':
ds@1952
   598
		case '4':
ds@1952
   599
		case '5':
ds@1952
   600
		case '6':
ds@1952
   601
		case '7':
ds@1952
   602
		case '8':
ds@1952
   603
		case '9':
ds@1952
   604
			{
ds@1952
   605
				n = 0;
ds@1952
   606
				do
ds@1952
   607
				{
ds@1952
   608
					n = 10 * n + (ch - '0');
ds@1952
   609
					ch = *fmt++;
ds@1952
   610
				} while(is_digit(ch));
ds@1952
   611
				width = n;
ds@1952
   612
				goto reswitch;
ds@1952
   613
			}
ds@1952
   614
		case 'c':
ds@1952
   615
			{
ds@1952
   616
				if (!llen)
ds@1952
   617
				{
ds@1952
   618
					goto done;
ds@1952
   619
				}
ds@1952
   620
				char *c = (char *)args[arg];
ds@1952
   621
				*buf_p++ = *c;
ds@1952
   622
				llen--;
ds@1952
   623
				arg++;
ds@1952
   624
				break;
ds@1952
   625
			}
ds@1952
   626
		case 'd':
ds@1952
   627
		case 'i':
ds@1952
   628
			{
ds@1952
   629
				int *value = (int *)args[arg];
ds@1952
   630
				AddInt(&buf_p, llen, *value, width, flags);
ds@1952
   631
				arg++;
ds@1952
   632
				break;
ds@1952
   633
			}
ds@1952
   634
		case 'u':
ds@1952
   635
			{
ds@1952
   636
				unsigned int *value = (unsigned int *)args[arg];
ds@1952
   637
				AddUInt(&buf_p, llen, *value, width, flags);
ds@1952
   638
				arg++;
ds@1952
   639
				break;
ds@1952
   640
			}
ds@1952
   641
		case 'f':
ds@1952
   642
			{
ds@1952
   643
				float *value = (float *)args[arg];
ds@1952
   644
				AddFloat(&buf_p, llen, *value, width, prec, flags);
ds@1952
   645
				arg++;
ds@1952
   646
				break;
ds@1952
   647
			}
ds@1952
   648
		case 's':
ds@1952
   649
			{
ds@1952
   650
				const char *str = (const char *)args[arg];
ds@1952
   651
				AddString(&buf_p, llen, str, width, prec);
ds@1952
   652
				arg++;
ds@1952
   653
				break;
ds@1952
   654
			}
ds@1952
   655
		case 'X':
ds@1952
   656
			{
ds@1952
   657
				unsigned int *value = (unsigned int *)args[arg];
ds@1952
   658
				flags |= UPPERDIGITS;
ds@1952
   659
				AddHex(&buf_p, llen, *value, width, flags);
ds@1952
   660
				arg++;
ds@1952
   661
				break;
ds@1952
   662
			}
ds@1952
   663
		case 'x':
ds@1952
   664
			{
ds@1952
   665
				unsigned int *value = (unsigned int *)args[arg];
ds@1952
   666
				AddHex(&buf_p, llen, *value, width, flags);
ds@1952
   667
				arg++;
ds@1952
   668
				break;
ds@1952
   669
			}
ds@1952
   670
		case '%':
ds@1952
   671
			{
ds@1952
   672
				if (!llen)
ds@1952
   673
				{
ds@1952
   674
					goto done;
ds@1952
   675
				}
ds@1952
   676
				*buf_p++ = ch;
ds@1952
   677
				llen--;
ds@1952
   678
				break;
ds@1952
   679
			}
ds@1952
   680
		case '\0':
ds@1952
   681
			{
ds@1952
   682
				if (!llen)
ds@1952
   683
				{
ds@1952
   684
					goto done;
ds@1952
   685
				}
ds@1952
   686
				*buf_p++ = '%';
ds@1952
   687
				llen--;
ds@1952
   688
				goto done;
ds@1952
   689
			}
ds@1952
   690
		default:
ds@1952
   691
			{
ds@1952
   692
				if (!llen)
ds@1952
   693
				{
ds@1952
   694
					goto done;
ds@1952
   695
				}
ds@1952
   696
				*buf_p++ = ch;
ds@1952
   697
				llen--;
ds@1952
   698
				break;
ds@1952
   699
			}
ds@1952
   700
		}
ds@1952
   701
	}
ds@1952
   702
ds@1952
   703
done:
ds@1952
   704
	*buf_p = '\0';
ds@1952
   705
ds@1952
   706
	return (maxlen - llen - 1);
ds@1952
   707
}
ds@1952
   708
ds@1952
   709
size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
ds@1952
   710
{
ds@1952
   711
	if (!buffer || !maxlen)
ds@1952
   712
	{
ds@1952
   713
		return 0;
ds@1952
   714
	}
ds@1952
   715
ds@1952
   716
	int arg;
ds@1952
   717
	int args = params[0];
ds@1952
   718
	char *buf_p;
ds@1952
   719
	char ch;
ds@1952
   720
	int flags;
ds@1952
   721
	int width;
ds@1952
   722
	int prec;
ds@1952
   723
	int n;
ds@1952
   724
	char sign;
ds@1952
   725
	const char *fmt;
ds@1952
   726
	size_t llen = maxlen - 1;
ds@1952
   727
ds@1952
   728
	buf_p = buffer;
ds@1952
   729
	arg = *param;
ds@1952
   730
	fmt = format;
ds@1952
   731
ds@1952
   732
	while (true)
ds@1952
   733
	{
ds@1952
   734
		// run through the format string until we hit a '%' or '\0'
ds@1952
   735
		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
ds@1952
   736
		{
ds@1952
   737
			*buf_p++ = ch;
ds@1952
   738
			llen--;
ds@1952
   739
		}
ds@1952
   740
		if ((ch == '\0') || (llen <= 0))
ds@1952
   741
		{
ds@1952
   742
			goto done;
ds@1952
   743
		}
ds@1952
   744
ds@1952
   745
		// skip over the '%'
ds@1952
   746
		fmt++;
ds@1952
   747
ds@1952
   748
		// reset formatting state
ds@1952
   749
		flags = 0;
ds@1952
   750
		width = 0;
ds@1952
   751
		prec = -1;
ds@1952
   752
		sign = '\0';
ds@1952
   753
ds@1952
   754
rflag:
ds@1952
   755
		ch = *fmt++;
ds@1952
   756
reswitch:
ds@1952
   757
		switch(ch)
ds@1952
   758
		{
ds@1952
   759
		case '-':
ds@1952
   760
			{
ds@1952
   761
				flags |= LADJUST;
ds@1952
   762
				goto rflag;
ds@1952
   763
			}
ds@1952
   764
		case '.':
ds@1952
   765
			{
ds@1952
   766
				n = 0;
ds@1952
   767
				while(is_digit((ch = *fmt++)))
ds@1952
   768
				{
ds@1952
   769
					n = 10 * n + (ch - '0');
ds@1952
   770
				}
ds@1952
   771
				prec = (n < 0) ? -1 : n;
ds@1952
   772
				goto reswitch;
ds@1952
   773
			}
ds@1952
   774
		case '0':
ds@1952
   775
			{
ds@1952
   776
				flags |= ZEROPAD;
ds@1952
   777
				goto rflag;
ds@1952
   778
			}
ds@1952
   779
		case '1':
ds@1952
   780
		case '2':
ds@1952
   781
		case '3':
ds@1952
   782
		case '4':
ds@1952
   783
		case '5':
ds@1952
   784
		case '6':
ds@1952
   785
		case '7':
ds@1952
   786
		case '8':
ds@1952
   787
		case '9':
ds@1952
   788
			{
ds@1952
   789
				n = 0;
ds@1952
   790
				do
ds@1952
   791
				{
ds@1952
   792
					n = 10 * n + (ch - '0');
ds@1952
   793
					ch = *fmt++;
ds@1952
   794
				} while(is_digit(ch));
ds@1952
   795
				width = n;
ds@1952
   796
				goto reswitch;
ds@1952
   797
			}
ds@1952
   798
		case 'c':
ds@1952
   799
			{
ds@1952
   800
				CHECK_ARGS(0);
ds@1952
   801
				if (!llen)
ds@1952
   802
				{
ds@1952
   803
					goto done;
ds@1952
   804
				}
ds@1952
   805
				char *c;
ds@1952
   806
				pCtx->LocalToString(params[arg], &c);
ds@1952
   807
				*buf_p++ = *c;
ds@1952
   808
				llen--;
ds@1952
   809
				arg++;
ds@1952
   810
				break;
ds@1952
   811
			}
ds@1952
   812
		case 'd':
ds@1952
   813
		case 'i':
ds@1952
   814
			{
ds@1952
   815
				CHECK_ARGS(0);
ds@1952
   816
				cell_t *value;
ds@1952
   817
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   818
				AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
ds@1952
   819
				arg++;
ds@1952
   820
				break;
ds@1952
   821
			}
ds@1952
   822
		case 'u':
ds@1952
   823
			{
ds@1952
   824
				CHECK_ARGS(0);
ds@1952
   825
				cell_t *value;
ds@1952
   826
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   827
				AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
ds@1952
   828
				arg++;
ds@1952
   829
				break;
ds@1952
   830
			}
ds@1952
   831
		case 'f':
ds@1952
   832
			{
ds@1952
   833
				CHECK_ARGS(0);
ds@1952
   834
				cell_t *value;
ds@1952
   835
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   836
				AddFloat(&buf_p, llen, sp_ctof(*value), width, prec, flags);
ds@1952
   837
				arg++;
ds@1952
   838
				break;
ds@1952
   839
			}
ds@1952
   840
		case 'L':
ds@1952
   841
			{
ds@1952
   842
				CHECK_ARGS(0);
ds@1952
   843
				cell_t *value;
ds@1952
   844
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   845
				char buffer[255];
ds@1952
   846
				if (*value)
ds@1952
   847
				{
ds@1952
   848
					CPlayer *player = g_Players.GetPlayerByIndex(*value);
ds@1952
   849
					if (!player || !player->IsConnected())
ds@1952
   850
					{
ds@1952
   851
						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
ds@1952
   852
					}
ds@1952
   853
					const char *auth = player->GetAuthString();
ds@1952
   854
					if (!auth || auth[0] == '\0')
ds@1952
   855
					{
ds@1952
   856
						auth = "STEAM_ID_PENDING";
ds@1952
   857
					}
ds@1952
   858
					int userid = engine->GetPlayerUserId(player->GetEdict());
ds@1952
   859
					UTIL_Format(buffer, 
ds@1952
   860
						sizeof(buffer), 
ds@1952
   861
						"%s<%d><%s><>", 
ds@1952
   862
						player->GetName(),
ds@1952
   863
						userid,
ds@1952
   864
						auth);
ds@1952
   865
				}
ds@1952
   866
				else
ds@1952
   867
				{
ds@1952
   868
					UTIL_Format(buffer,
ds@1952
   869
						sizeof(buffer),
ds@1952
   870
						"Console<0><Console><Console>");
ds@1952
   871
				}
ds@1952
   872
				AddString(&buf_p, llen, buffer, width, prec);
ds@1952
   873
				arg++;
ds@1952
   874
				break;
ds@1952
   875
			}
ds@1952
   876
		case 'N':
ds@1952
   877
			{
ds@1952
   878
				CHECK_ARGS(0);
ds@1952
   879
				cell_t *value;
ds@1952
   880
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   881
ds@1952
   882
				const char *name = "Console";
ds@1952
   883
				if (*value)
ds@1952
   884
				{
ds@1952
   885
					CPlayer *player = g_Players.GetPlayerByIndex(*value);
ds@1952
   886
					if (!player || !player->IsConnected())
ds@1952
   887
					{
ds@1952
   888
						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
ds@1952
   889
					}
ds@1952
   890
					name = player->GetName();
ds@1952
   891
				}
ds@1952
   892
				AddString(&buf_p, llen, name, width, prec);
ds@1952
   893
				arg++;
ds@1952
   894
				break;
ds@1952
   895
			}
ds@1952
   896
		case 's':
ds@1952
   897
			{
ds@1952
   898
				CHECK_ARGS(0);
ds@1952
   899
				char *str;
ds@1952
   900
				int err;
ds@1952
   901
				if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
ds@1952
   902
				{
ds@1952
   903
					pCtx->ThrowNativeErrorEx(err, "Could not deference string");
ds@1952
   904
					return 0;
ds@1952
   905
				}
ds@1952
   906
				AddString(&buf_p, llen, str, width, prec);
ds@1952
   907
				arg++;
ds@1952
   908
				break;
ds@1952
   909
			}
ds@1952
   910
		case 'T':
ds@1952
   911
			{
ds@1952
   912
				CHECK_ARGS(1);
ds@1952
   913
				char *key;
ds@1952
   914
				bool error;
ds@1952
   915
				size_t res;
ds@1952
   916
				cell_t *target;
ds@1952
   917
				pCtx->LocalToString(params[arg++], &key);
ds@1952
   918
				pCtx->LocalToPhysAddr(params[arg++], &target);
ds@1952
   919
				res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
ds@1952
   920
				if (error)
ds@1952
   921
				{
ds@1952
   922
					return 0;
ds@1952
   923
				}
ds@1952
   924
				buf_p += res;
ds@1952
   925
				llen -= res;
ds@1952
   926
				break;
ds@1952
   927
			}
ds@1952
   928
		case 't':
ds@1952
   929
			{
ds@1952
   930
				CHECK_ARGS(0);
ds@1952
   931
				char *key;
ds@1952
   932
				bool error;
ds@1952
   933
				size_t res;
ds@1952
   934
				cell_t target = g_SourceMod.GetGlobalTarget();
ds@1952
   935
				pCtx->LocalToString(params[arg++], &key);
ds@1952
   936
				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
ds@1952
   937
				if (error)
ds@1952
   938
				{
ds@1952
   939
					return 0;
ds@1952
   940
				}
ds@1952
   941
				buf_p += res;
ds@1952
   942
				llen -= res;
ds@1952
   943
				break;
ds@1952
   944
			}
ds@1952
   945
		case 'X':
ds@1952
   946
			{
ds@1952
   947
				CHECK_ARGS(0);
ds@1952
   948
				cell_t *value;
ds@1952
   949
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   950
				flags |= UPPERDIGITS;
ds@1952
   951
				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
ds@1952
   952
				arg++;
ds@1952
   953
				break;
ds@1952
   954
			}
ds@1952
   955
		case 'x':
ds@1952
   956
			{
ds@1952
   957
				CHECK_ARGS(0);
ds@1952
   958
				cell_t *value;
ds@1952
   959
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
   960
				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
ds@1952
   961
				arg++;
ds@1952
   962
				break;
ds@1952
   963
			}
ds@1952
   964
		case '%':
ds@1952
   965
			{
ds@1952
   966
				if (!llen)
ds@1952
   967
				{
ds@1952
   968
					goto done;
ds@1952
   969
				}
ds@1952
   970
				*buf_p++ = ch;
ds@1952
   971
				llen--;
ds@1952
   972
				break;
ds@1952
   973
			}
ds@1952
   974
		case '\0':
ds@1952
   975
			{
ds@1952
   976
				if (!llen)
ds@1952
   977
				{
ds@1952
   978
					goto done;
ds@1952
   979
				}
ds@1952
   980
				*buf_p++ = '%';
ds@1952
   981
				llen--;
ds@1952
   982
				goto done;
ds@1952
   983
			}
ds@1952
   984
		default:
ds@1952
   985
			{
ds@1952
   986
				if (!llen)
ds@1952
   987
				{
ds@1952
   988
					goto done;
ds@1952
   989
				}
ds@1952
   990
				*buf_p++ = ch;
ds@1952
   991
				llen--;
ds@1952
   992
				break;
ds@1952
   993
			}
ds@1952
   994
		}
ds@1952
   995
	}
ds@1952
   996
ds@1952
   997
done:
ds@1952
   998
	*buf_p = '\0';
ds@1952
   999
	*param = arg;
ds@1952
  1000
	return (maxlen - llen - 1);
ds@1952
  1001
}
ds@1952
  1002
ds@1952
  1003
const char *stristr(const char *str, const char *substr)
ds@1952
  1004
{
ds@1952
  1005
	if (!*substr)
ds@1952
  1006
	{
ds@1952
  1007
		return ((char *)str);
ds@1952
  1008
	}
ds@1952
  1009
ds@1952
  1010
	char *needle = (char *)substr;
ds@1952
  1011
	char *prevloc = (char *)str;
ds@1952
  1012
	char *haystack = (char *)str;
ds@1952
  1013
ds@1952
  1014
	while (*haystack)
ds@1952
  1015
	{
ds@1952
  1016
		if (tolower(*haystack) == tolower(*needle))
ds@1952
  1017
		{
ds@1952
  1018
			haystack++;
ds@1952
  1019
			if (!*++needle)
ds@1952
  1020
			{
ds@1952
  1021
				return prevloc;
ds@1952
  1022
			}
ds@1952
  1023
		}
ds@1952
  1024
		else
ds@1952
  1025
		{
ds@1952
  1026
			haystack = ++prevloc;
ds@1952
  1027
			needle = (char *)substr;
ds@1952
  1028
		}
ds@1952
  1029
	}
ds@1952
  1030
ds@1952
  1031
	return NULL;
ds@1952
  1032
}
ds@1952
  1033
ds@1952
  1034
unsigned int strncopy(char *dest, const char *src, size_t count)
ds@1952
  1035
{
ds@1952
  1036
	if (!count)
ds@1952
  1037
	{
ds@1952
  1038
		return 0;
ds@1952
  1039
	}
ds@1952
  1040
ds@1952
  1041
	char *start = dest;
ds@1952
  1042
	while ((*src) && (--count))
ds@1952
  1043
	{
ds@1952
  1044
		*dest++ = *src++;
ds@1952
  1045
	}
ds@1952
  1046
	*dest = '\0';
ds@1952
  1047
ds@1952
  1048
	return (dest - start);
ds@1952
  1049
}
ds@1952
  1050
ds@1952
  1051
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
ds@1952
  1052
{
ds@1952
  1053
	va_list ap;
ds@1952
  1054
	va_start(ap, fmt);
ds@1952
  1055
	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
ds@1952
  1056
	va_end(ap);
ds@1952
  1057
ds@1952
  1058
	if (len >= maxlength)
ds@1952
  1059
	{
ds@1952
  1060
		buffer[maxlength - 1] = '\0';
ds@1952
  1061
		return (maxlength - 1);
ds@1952
  1062
	}
ds@1952
  1063
	else
ds@1952
  1064
	{
ds@1952
  1065
		return len;
ds@1952
  1066
	}
ds@1952
  1067
}
ds@1952
  1068
ds@1952
  1069
size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
ds@1952
  1070
{
ds@1952
  1071
	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
ds@1952
  1072
ds@1952
  1073
	if (len >= maxlength)
ds@1952
  1074
	{
ds@1952
  1075
		buffer[maxlength - 1] = '\0';
ds@1952
  1076
		return (maxlength - 1);
ds@1952
  1077
	}
ds@1952
  1078
	else
ds@1952
  1079
	{
ds@1952
  1080
		return len;
ds@1952
  1081
	}
ds@1952
  1082
}
ds@1952
  1083
ds@1952
  1084
char *sm_strdup(const char *str)
ds@1952
  1085
{
ds@1952
  1086
	char *ptr = new char[strlen(str)+1];
ds@1952
  1087
	strcpy(ptr, str);
ds@1952
  1088
	return ptr;
ds@1952
  1089
}
ds@1952
  1090
ds@1952
  1091
unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
ds@1952
  1092
{
ds@1952
  1093
	size_t searchLen = strlen(search);
ds@1952
  1094
	size_t replaceLen = strlen(replace);
ds@1952
  1095
ds@1952
  1096
	char *ptr = subject;
ds@1952
  1097
	unsigned int total = 0;
ds@1952
  1098
	while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
ds@1952
  1099
	{
ds@1952
  1100
		total++;
ds@1952
  1101
		if (*ptr == '\0')
ds@1952
  1102
		{
ds@1952
  1103
			break;
ds@1952
  1104
		}
ds@1952
  1105
	}
ds@1952
  1106
ds@1952
  1107
	return total;
ds@1952
  1108
}
ds@1952
  1109
ds@1952
  1110
/**
ds@1952
  1111
 * NOTE: Do not edit this for the love of god unless you have 
ds@1952
  1112
 * read the test cases and understand the code behind each one.
ds@1952
  1113
 * While I don't guarantee there aren't mistakes, I do guarantee
ds@1952
  1114
 * that plugins will end up relying on tiny idiosyncracies of this
ds@1952
  1115
 * function, just like they did with AMX Mod X.
ds@1952
  1116
 *
ds@1952
  1117
 * There are explicitly more cases than the AMX Mod X version because
ds@1952
  1118
 * we're not doing a blind copy.  Each case is specifically optimized
ds@1952
  1119
 * for what needs to be done.  Even better, we don't have to error on
ds@1952
  1120
 * bad buffer sizes.  Instead, this function will smartly cut off the 
ds@1952
  1121
 * string in a way that pushes old data out.
ds@1952
  1122
 */
ds@1952
  1123
char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
ds@1952
  1124
{
ds@1952
  1125
	char *ptr = subject;
ds@1952
  1126
	size_t browsed = 0;
ds@1952
  1127
	size_t textLen = strlen(subject);
ds@1952
  1128
ds@1952
  1129
	/* It's not possible to search or replace */
ds@1952
  1130
	if (searchLen > textLen)
ds@1952
  1131
	{
ds@1952
  1132
		return NULL;
ds@1952
  1133
	}
ds@1952
  1134
ds@1952
  1135
	/* Handle the case of one byte replacement.
ds@1952
  1136
	 * It's only valid in one case.
ds@1952
  1137
	 */
ds@1952
  1138
	if (maxLen == 1)
ds@1952
  1139
	{
ds@1952
  1140
		/* If the search matches and the replace length is 0, 
ds@1952
  1141
		 * we can just terminate the string and be done.
ds@1952
  1142
		 */
ds@1952
  1143
		if (strcmp(subject, search) == 0 && replaceLen == 0)
ds@1952
  1144
		{
ds@1952
  1145
			*subject = '\0';
ds@1952
  1146
			return subject;
ds@1952
  1147
		}
ds@1952
  1148
		else
ds@1952
  1149
		{
ds@1952
  1150
			return NULL;
ds@1952
  1151
		}
ds@1952
  1152
	}
ds@1952
  1153
ds@1952
  1154
	/* Subtract one off the maxlength so we can include the null terminator */
ds@1952
  1155
	maxLen--;
ds@1952
  1156
ds@1952
  1157
	while (*ptr != '\0' && (browsed <= textLen - searchLen))
ds@1952
  1158
	{
ds@1952
  1159
		/* See if we get a comparison */
ds@1952
  1160
		if (strncmp(ptr, search, searchLen) == 0)
ds@1952
  1161
		{
ds@1952
  1162
			if (replaceLen > searchLen)
ds@1952
  1163
			{
ds@1952
  1164
				/* First, see if we have enough space to do this operation */
ds@1952
  1165
				if (maxLen - textLen < replaceLen - searchLen)
ds@1952
  1166
				{
ds@1952
  1167
					/* First, see if the replacement length goes out of bounds. */
ds@1952
  1168
					if (browsed + replaceLen >= maxLen)
ds@1952
  1169
					{
ds@1952
  1170
						/* EXAMPLE CASE:
ds@1952
  1171
						 * Subject: AABBBCCC
ds@1952
  1172
						 * Buffer : 12 bytes
ds@1952
  1173
						 * Search : BBB
ds@1952
  1174
						 * Replace: DDDDDDDDDD
ds@1952
  1175
						 * OUTPUT : AADDDDDDDDD
ds@1952
  1176
						 * POSITION:           ^
ds@1952
  1177
						 */
ds@1952
  1178
						/* If it does, we'll just bound the length and do a strcpy. */
ds@1952
  1179
						replaceLen = maxLen - browsed;
ds@1952
  1180
						/* Note, we add one to the final result for the null terminator */
ds@1952
  1181
						strncopy(ptr, replace, replaceLen+1);
ds@1952
  1182
					}
ds@1952
  1183
					else
ds@1952
  1184
					{
ds@1952
  1185
						/* EXAMPLE CASE:
ds@1952
  1186
						 * Subject: AABBBCCC
ds@1952
  1187
						 * Buffer : 12 bytes
ds@1952
  1188
						 * Search : BBB
ds@1952
  1189
						 * Replace: DDDDDDD
ds@1952
  1190
						 * OUTPUT : AADDDDDDDCC
ds@1952
  1191
						 * POSITION:         ^
ds@1952
  1192
						 */
ds@1952
  1193
						/* We're going to have some bytes left over... */
ds@1952
  1194
						size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
ds@1952
  1195
						size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
ds@1952
  1196
						char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
ds@1952
  1197
						char *moveTo = ptr + replaceLen;
ds@1952
  1198
ds@1952
  1199
						/* First, move our old data out of the way. */
ds@1952
  1200
						memmove(moveTo, moveFrom, realBytesToCopy);
ds@1952
  1201
ds@1952
  1202
						/* Now, do our replacement. */
ds@1952
  1203
						memcpy(ptr, replace, replaceLen);
ds@1952
  1204
					}
ds@1952
  1205
				}
ds@1952
  1206
				else
ds@1952
  1207
				{
ds@1952
  1208
					/* EXAMPLE CASE:
ds@1952
  1209
					 * Subject: AABBBCCC
ds@1952
  1210
					 * Buffer : 12 bytes
ds@1952
  1211
					 * Search : BBB
ds@1952
  1212
					 * Replace: DDDD
ds@1952
  1213
					 * OUTPUT : AADDDDCCC
ds@1952
  1214
					 * POSITION:      ^
ds@1952
  1215
					 */
ds@1952
  1216
					/* Yes, we have enough space.  Do a normal move operation. */
ds@1952
  1217
					char *moveFrom = ptr + searchLen;
ds@1952
  1218
					char *moveTo = ptr + replaceLen;
ds@1952
  1219
ds@1952
  1220
					/* First move our old data out of the way. */
ds@1952
  1221
					size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
ds@1952
  1222
					memmove(moveTo, moveFrom, bytesToCopy);
ds@1952
  1223
ds@1952
  1224
					/* Now do our replacement. */
ds@1952
  1225
					memcpy(ptr, replace, replaceLen);
ds@1952
  1226
				}
ds@1952
  1227
			}
ds@1952
  1228
			else if (replaceLen < searchLen)
ds@1952
  1229
			{
ds@1952
  1230
				/* EXAMPLE CASE:
ds@1952
  1231
				 * Subject: AABBBCCC
ds@1952
  1232
				 * Buffer : 12 bytes
ds@1952
  1233
				 * Search : BBB
ds@1952
  1234
				 * Replace: D
ds@1952
  1235
				 * OUTPUT : AADCCC
ds@1952
  1236
				 * POSOTION:   ^
ds@1952
  1237
				 */
ds@1952
  1238
				/* If the replacement does not grow the string length, we do not
ds@1952
  1239
				 * need to do any fancy checking at all.  Yay!
ds@1952
  1240
				 */
ds@1952
  1241
				char *moveFrom = ptr + searchLen;		/* Start after the search pointer */
ds@1952
  1242
				char *moveTo = ptr + replaceLen;		/* Copy to where the replacement ends */
ds@1952
  1243
ds@1952
  1244
				/* Copy our replacement in, if any */
ds@1952
  1245
				if (replaceLen)
ds@1952
  1246
				{
ds@1952
  1247
					memcpy(ptr, replace, replaceLen);
ds@1952
  1248
				}
ds@1952
  1249
ds@1952
  1250
				/* Figure out how many bytes to move down, including null terminator */
ds@1952
  1251
				size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
ds@1952
  1252
ds@1952
  1253
				/* Move the rest of the string down */
ds@1952
  1254
				memmove(moveTo, moveFrom, bytesToCopy);
ds@1952
  1255
			}
ds@1952
  1256
			else
ds@1952
  1257
			{
ds@1952
  1258
				/* EXAMPLE CASE:
ds@1952
  1259
				 * Subject: AABBBCCC
ds@1952
  1260
				 * Buffer : 12 bytes
ds@1952
  1261
				 * Search : BBB
ds@1952
  1262
				 * Replace: DDD
ds@1952
  1263
				 * OUTPUT : AADDDCCC
ds@1952
  1264
				 * POSITION:     ^
ds@1952
  1265
				 */
ds@1952
  1266
				/* We don't have to move anything around, just do a straight copy */
ds@1952
  1267
				memcpy(ptr, replace, replaceLen);
ds@1952
  1268
			}
ds@1952
  1269
ds@1952
  1270
			return ptr + replaceLen;
ds@1952
  1271
		}
ds@1952
  1272
		ptr++;
ds@1952
  1273
		browsed++;
ds@1952
  1274
	}
ds@1952
  1275
ds@1952
  1276
	return NULL;
ds@1952
  1277
}
ds@1952
  1278
ds@1952
  1279
char *UTIL_TrimWhitespace(char *str, size_t &len)
ds@1952
  1280
{
ds@1952
  1281
	char *end = str + len - 1;
ds@1952
  1282
ds@1952
  1283
	if (!len)
ds@1952
  1284
	{
ds@1952
  1285
		return str;
ds@1952
  1286
	}
ds@1952
  1287
ds@1952
  1288
	/* Iterate backwards through string until we reach first non-whitespace char */
ds@1952
  1289
	while (end >= str && textparsers->IsWhitespace(end))
ds@1952
  1290
	{
ds@1952
  1291
		end--;
ds@1952
  1292
		len--;
ds@1952
  1293
	}
ds@1952
  1294
ds@1952
  1295
	/* Replace first whitespace char (at the end) with null terminator.
ds@1952
  1296
	 * If there is none, we're just replacing the null terminator. 
ds@1952
  1297
	 */
ds@1952
  1298
	*(end + 1) = '\0';
ds@1952
  1299
ds@1952
  1300
	while (*str != '\0' && textparsers->IsWhitespace(str))
ds@1952
  1301
	{
ds@1952
  1302
		str++;
ds@1952
  1303
		len--;
ds@1952
  1304
	}
ds@1952
  1305
ds@1952
  1306
	return str;
ds@1952
  1307
}
ds@1952
  1308