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