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