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