diff -r c7b4237fff50 -r 03a67e41afbc core/sm_stringutil.cpp
--- a/core/sm_stringutil.cpp Fri Mar 28 06:21:34 2008 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1277 +0,0 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * SourceMod
- * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
- * =============================================================================
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, version 3.0, as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
- *
- * As a special exception, AlliedModders LLC gives you permission to link the
- * code of this program (as well as its derivative works) to "Half-Life 2," the
- * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
- * by the Valve Corporation. You must obey the GNU General Public License in
- * all respects for all other code used. Additionally, AlliedModders LLC grants
- * this exception to all derivative works. AlliedModders LLC defines further
- * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
- * or .
- *
- * Version: $Id$
- */
-
-#include
-#include
-#include
-#include "sm_stringutil.h"
-#include "Logger.h"
-#include "PluginSys.h"
-#include "Translator.h"
-#include "PlayerManager.h"
-
-#define LADJUST 0x00000004 /* left adjustment */
-#define ZEROPAD 0x00000080 /* zero (as opposed to blank) pad */
-#define UPPERDIGITS 0x00000200 /* make alpha digits uppercase */
-#define to_digit(c) ((c) - '0')
-#define is_digit(c) ((unsigned)to_digit(c) <= 9)
-
-#define CHECK_ARGS(x) \
- if ((arg+x) > args) { \
- pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
- return 0; \
- }
-
-size_t CorePlayerTranslate(int client, char *buffer, size_t maxlength, const char *phrase, void **params)
-{
- Translation pTrans;
- TransError err;
-
- err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetClientLanguage(client), &pTrans);
- if (err != Trans_Okay)
- {
- err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetServerLanguage(), &pTrans);
- if (err != Trans_Okay && g_Translator.GetServerLanguage() != CORELANG_ENGLISH)
- {
- err = g_pCorePhrases->GetTranslation(phrase, CORELANG_ENGLISH, &pTrans);
- }
- }
-
- if (err != Trans_Okay)
- {
- return UTIL_Format(buffer, maxlength, "%s", phrase);
- }
-
- return g_Translator.Translate(buffer, maxlength, params, &pTrans);
-}
-
-inline bool TryTranslation(CPlugin *pl, const char *key, unsigned int langid, unsigned int langcount, Translation *pTrans)
-{
- TransError err = Trans_BadLanguage;
- CPhraseFile *phrfl;
-
- for (size_t i=0; iGetLangFileByIndex(i));
- err = phrfl->GetTranslation(key, langid, pTrans);
- }
-
- return (err == Trans_Okay) ? true : false;
-}
-
-inline void ReorderTranslationParams(const Translation *pTrans, cell_t *params)
-{
- cell_t new_params[MAX_TRANSLATE_PARAMS];
- for (unsigned int i = 0; i < pTrans->fmt_count; i++)
- {
- new_params[i] = params[pTrans->fmt_order[i]];
- }
- memcpy(params, new_params, pTrans->fmt_count * sizeof(cell_t));
-}
-
-size_t Translate(char *buffer,
- size_t maxlen,
- IPluginContext *pCtx,
- const char *key,
- cell_t target,
- const cell_t *params,
- int *arg,
- bool *error)
-{
- unsigned int langid;
- *error = false;
- Translation pTrans;
- CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
- size_t langcount = pl->GetLangFileCount();
- unsigned int max_params = 0;
-
-try_serverlang:
- if (target == LANG_SERVER)
- {
- langid = g_Translator.GetServerLanguage();
- }
- else if ((target >= 1) && (target <= g_Players.GetMaxClients()))
- {
- langid = g_Translator.GetClientLanguage(target);
- }
- else
- {
- pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
- goto error_out;
- }
-
- if (!TryTranslation(pl, key, langid, langcount, &pTrans))
- {
- if (target != LANG_SERVER && langid != g_Translator.GetServerLanguage())
- {
- target = LANG_SERVER;
- goto try_serverlang;
- }
- else if (langid != LANGUAGE_ENGLISH)
- {
- if (!TryTranslation(pl, key, LANGUAGE_ENGLISH, langcount, &pTrans))
- {
- pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
- goto error_out;
- }
- }
- else
- {
- pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
- goto error_out;
- }
- }
-
- max_params = pTrans.fmt_count;
-
- if (max_params)
- {
- cell_t new_params[MAX_TRANSLATE_PARAMS];
-
- /* Check if we're going to over the limit */
- if ((*arg) + (max_params - 1) > (size_t)params[0])
- {
- pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX,
- "Translation string formatted incorrectly - missing at least %d parameters",
- ((*arg + (max_params - 1)) - params[0])
- );
- goto error_out;
- }
-
- /* If we need to re-order the parameters, do so with a temporary array.
- * Otherwise, we could run into trouble with continual formats, a la ShowActivity().
- */
- memcpy(new_params, params, sizeof(cell_t) * (params[0] + 1));
- ReorderTranslationParams(&pTrans, &new_params[*arg]);
-
- return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, new_params, arg);
- }
- else
- {
- return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, params, arg);
- }
-
-error_out:
- *error = true;
- return 0;
-}
-
-void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
-{
- int size = 0;
- char *buf;
- static char nlstr[] = {'(','n','u','l','l',')','\0'};
-
- buf = *buf_p;
-
- if (string == NULL)
- {
- string = nlstr;
- prec = -1;
- }
-
- if (prec >= 0)
- {
- for (size = 0; size < prec; size++)
- {
- if (string[size] == '\0')
- {
- break;
- }
- }
- }
- else
- {
- while (string[size++]);
- size--;
- }
-
- if (size > (int)maxlen)
- {
- size = maxlen;
- }
-
- maxlen -= size;
- width -= size;
-
- while (size--)
- {
- *buf++ = *string++;
- }
-
- while ((width-- > 0) && maxlen)
- {
- *buf++ = ' ';
- maxlen--;
- }
-
- *buf_p = buf;
-}
-
-void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec, int flags)
-{
- int digits; // non-fraction part digits
- double tmp; // temporary
- char *buf = *buf_p; // output buffer pointer
- int val; // temporary
- int sign = 0; // 0: positive, 1: negative
- int fieldlength; // for padding
- int significant_digits = 0; // number of significant digits written
- const int MAX_SIGNIFICANT_DIGITS = 16;
-
- // default precision
- if (prec < 0)
- {
- prec = 6;
- }
-
- // get the sign
- if (fval < 0)
- {
- fval = -fval;
- sign = 1;
- }
-
- // compute whole-part digits count
- digits = (int)log10(fval) + 1;
-
- // Only print 0.something if 0 < fval < 1
- if (digits < 1)
- {
- digits = 1;
- }
-
- // compute the field length
- fieldlength = digits + prec + ((prec > 0) ? 1 : 0) + sign;
-
- // minus sign BEFORE left padding if padding with zeros
- if (sign && maxlen && (flags & ZEROPAD))
- {
- *buf++ = '-';
- maxlen--;
- }
-
- // right justify if required
- if ((flags & LADJUST) == 0)
- {
- while ((fieldlength < width) && maxlen)
- {
- *buf++ = (flags & ZEROPAD) ? '0' : ' ';
- width--;
- maxlen--;
- }
- }
-
- // minus sign AFTER left padding if padding with spaces
- if (sign && maxlen && !(flags & ZEROPAD))
- {
- *buf++ = '-';
- maxlen--;
- }
-
- // write the whole part
- tmp = pow(10.0, digits-1);
- while ((digits--) && maxlen)
- {
- if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
- {
- *buf++ = '0';
- }
- else
- {
- val = (int)(fval / tmp);
- *buf++ = '0' + val;
- fval -= val * tmp;
- tmp *= 0.1;
- }
- maxlen--;
- }
-
- // write the fraction part
- if (maxlen)
- {
- *buf++ = '.';
- maxlen--;
- }
-
- tmp = pow(10.0, prec);
-
- fval *= tmp;
- while (prec-- && maxlen)
- {
- if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
- {
- *buf++ = '0';
- }
- else
- {
- tmp *= 0.1;
- val = (int)(fval / tmp);
- *buf++ = '0' + val;
- fval -= val * tmp;
- }
- maxlen--;
- }
-
- // left justify if required
- if (flags & LADJUST)
- {
- while ((fieldlength < width) && maxlen)
- {
- // right-padding only with spaces, ZEROPAD is ignored
- *buf++ = ' ';
- width--;
- maxlen--;
- }
- }
-
- // update parent's buffer pointer
- *buf_p = buf;
-}
-
-void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
-{
- char text[32];
- int digits;
- char *buf;
-
- digits = 0;
- do
- {
- text[digits++] = '0' + val % 10;
- val /= 10;
- } while (val);
-
- buf = *buf_p;
-
- if (!(flags & LADJUST))
- {
- while (digits < width && maxlen)
- {
- *buf++ = (flags & ZEROPAD) ? '0' : ' ';
- width--;
- maxlen--;
- }
- }
-
- while (digits-- && maxlen)
- {
- *buf++ = text[digits];
- width--;
- maxlen--;
- }
-
- if (flags & LADJUST)
- {
- while (width-- && maxlen)
- {
- *buf++ = (flags & ZEROPAD) ? '0' : ' ';
- maxlen--;
- }
- }
-
- *buf_p = buf;
-}
-
-void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
-{
- char text[32];
- int digits;
- int signedVal;
- char *buf;
- unsigned int unsignedVal;
-
- digits = 0;
- signedVal = val;
- if (val < 0)
- {
- /* we want the unsigned version */
- unsignedVal = abs(val);
- }
- else
- {
- unsignedVal = val;
- }
-
- do
- {
- text[digits++] = '0' + unsignedVal % 10;
- unsignedVal /= 10;
- } while (unsignedVal);
-
- if (signedVal < 0)
- {
- text[digits++] = '-';
- }
-
- buf = *buf_p;
-
- if (!(flags & LADJUST))
- {
- while ((digits < width) && maxlen)
- {
- *buf++ = (flags & ZEROPAD) ? '0' : ' ';
- width--;
- maxlen--;
- }
- }
-
- while (digits-- && maxlen)
- {
- *buf++ = text[digits];
- width--;
- maxlen--;
- }
-
- if (flags & LADJUST)
- {
- while (width-- && maxlen)
- {
- *buf++ = (flags & ZEROPAD) ? '0' : ' ';
- maxlen--;
- }
- }
-
- *buf_p = buf;
-}
-
-void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
-{
- char text[32];
- int digits;
- char *buf;
- char digit;
- int hexadjust;
-
- if (flags & UPPERDIGITS)
- {
- hexadjust = 'A' - '9' - 1;
- }
- else
- {
- hexadjust = 'a' - '9' - 1;
- }
-
- digits = 0;
- do
- {
- digit = ('0' + val % 16);
- if (digit > '9')
- {
- digit += hexadjust;
- }
-
- text[digits++] = digit;
- val /= 16;
- } while(val);
-
- buf = *buf_p;
-
- if (!(flags & LADJUST))
- {
- while (digits < width && maxlen)
- {
- *buf++ = (flags & ZEROPAD) ? '0' : ' ';
- width--;
- maxlen--;
- }
- }
-
- while (digits-- && maxlen)
- {
- *buf++ = text[digits];
- width--;
- maxlen--;
- }
-
- if (flags & LADJUST)
- {
- while (width-- && maxlen)
- {
- *buf++ = (flags & ZEROPAD) ? '0' : ' ';
- maxlen--;
- }
- }
-
- *buf_p = buf;
-}
-
-size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
-{
- if (!buffer || !maxlen)
- {
- return 0;
- }
-
- int arg = 0;
- char *buf_p;
- char ch;
- int flags;
- int width;
- int prec;
- int n;
- char sign;
- const char *fmt;
- size_t llen = maxlen - 1;
-
- buf_p = buffer;
- fmt = format;
-
- while (true)
- {
- // run through the format string until we hit a '%' or '\0'
- for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
- {
- *buf_p++ = ch;
- llen--;
- }
- if ((ch == '\0') || (llen <= 0))
- {
- goto done;
- }
-
- // skip over the '%'
- fmt++;
-
- // reset formatting state
- flags = 0;
- width = 0;
- prec = -1;
- sign = '\0';
-
-rflag:
- ch = *fmt++;
-reswitch:
- switch(ch)
- {
- case '-':
- {
- flags |= LADJUST;
- goto rflag;
- }
- case '.':
- {
- n = 0;
- while(is_digit((ch = *fmt++)))
- {
- n = 10 * n + (ch - '0');
- }
- prec = (n < 0) ? -1 : n;
- goto reswitch;
- }
- case '0':
- {
- flags |= ZEROPAD;
- goto rflag;
- }
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- n = 0;
- do
- {
- n = 10 * n + (ch - '0');
- ch = *fmt++;
- } while(is_digit(ch));
- width = n;
- goto reswitch;
- }
- case 'c':
- {
- if (!llen)
- {
- goto done;
- }
- char *c = (char *)args[arg];
- *buf_p++ = *c;
- llen--;
- arg++;
- break;
- }
- case 'd':
- case 'i':
- {
- int *value = (int *)args[arg];
- AddInt(&buf_p, llen, *value, width, flags);
- arg++;
- break;
- }
- case 'u':
- {
- unsigned int *value = (unsigned int *)args[arg];
- AddUInt(&buf_p, llen, *value, width, flags);
- arg++;
- break;
- }
- case 'f':
- {
- float *value = (float *)args[arg];
- AddFloat(&buf_p, llen, *value, width, prec, flags);
- arg++;
- break;
- }
- case 's':
- {
- const char *str = (const char *)args[arg];
- AddString(&buf_p, llen, str, width, prec);
- arg++;
- break;
- }
- case 'X':
- {
- unsigned int *value = (unsigned int *)args[arg];
- flags |= UPPERDIGITS;
- AddHex(&buf_p, llen, *value, width, flags);
- arg++;
- break;
- }
- case 'x':
- {
- unsigned int *value = (unsigned int *)args[arg];
- AddHex(&buf_p, llen, *value, width, flags);
- arg++;
- break;
- }
- case '%':
- {
- if (!llen)
- {
- goto done;
- }
- *buf_p++ = ch;
- llen--;
- break;
- }
- case '\0':
- {
- if (!llen)
- {
- goto done;
- }
- *buf_p++ = '%';
- llen--;
- goto done;
- }
- default:
- {
- if (!llen)
- {
- goto done;
- }
- *buf_p++ = ch;
- llen--;
- break;
- }
- }
- }
-
-done:
- *buf_p = '\0';
-
- return (maxlen - llen - 1);
-}
-
-size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
-{
- if (!buffer || !maxlen)
- {
- return 0;
- }
-
- int arg;
- int args = params[0];
- char *buf_p;
- char ch;
- int flags;
- int width;
- int prec;
- int n;
- char sign;
- const char *fmt;
- size_t llen = maxlen - 1;
-
- buf_p = buffer;
- arg = *param;
- fmt = format;
-
- while (true)
- {
- // run through the format string until we hit a '%' or '\0'
- for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
- {
- *buf_p++ = ch;
- llen--;
- }
- if ((ch == '\0') || (llen <= 0))
- {
- goto done;
- }
-
- // skip over the '%'
- fmt++;
-
- // reset formatting state
- flags = 0;
- width = 0;
- prec = -1;
- sign = '\0';
-
-rflag:
- ch = *fmt++;
-reswitch:
- switch(ch)
- {
- case '-':
- {
- flags |= LADJUST;
- goto rflag;
- }
- case '.':
- {
- n = 0;
- while(is_digit((ch = *fmt++)))
- {
- n = 10 * n + (ch - '0');
- }
- prec = (n < 0) ? -1 : n;
- goto reswitch;
- }
- case '0':
- {
- flags |= ZEROPAD;
- goto rflag;
- }
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- n = 0;
- do
- {
- n = 10 * n + (ch - '0');
- ch = *fmt++;
- } while(is_digit(ch));
- width = n;
- goto reswitch;
- }
- case 'c':
- {
- CHECK_ARGS(0);
- if (!llen)
- {
- goto done;
- }
- char *c;
- pCtx->LocalToString(params[arg], &c);
- *buf_p++ = *c;
- llen--;
- arg++;
- break;
- }
- case 'd':
- case 'i':
- {
- CHECK_ARGS(0);
- cell_t *value;
- pCtx->LocalToPhysAddr(params[arg], &value);
- AddInt(&buf_p, llen, static_cast(*value), width, flags);
- arg++;
- break;
- }
- case 'u':
- {
- CHECK_ARGS(0);
- cell_t *value;
- pCtx->LocalToPhysAddr(params[arg], &value);
- AddUInt(&buf_p, llen, static_cast(*value), width, flags);
- arg++;
- break;
- }
- case 'f':
- {
- CHECK_ARGS(0);
- cell_t *value;
- pCtx->LocalToPhysAddr(params[arg], &value);
- AddFloat(&buf_p, llen, sp_ctof(*value), width, prec, flags);
- arg++;
- break;
- }
- case 'L':
- {
- CHECK_ARGS(0);
- cell_t *value;
- pCtx->LocalToPhysAddr(params[arg], &value);
- char buffer[255];
- if (*value)
- {
- CPlayer *player = g_Players.GetPlayerByIndex(*value);
- if (!player || !player->IsConnected())
- {
- return pCtx->ThrowNativeError("Client index %d is invalid", *value);
- }
- const char *auth = player->GetAuthString();
- if (!auth || auth[0] == '\0')
- {
- auth = "STEAM_ID_PENDING";
- }
- int userid = engine->GetPlayerUserId(player->GetEdict());
- UTIL_Format(buffer,
- sizeof(buffer),
- "%s<%d><%s><>",
- player->GetName(),
- userid,
- auth);
- }
- else
- {
- UTIL_Format(buffer,
- sizeof(buffer),
- "Console<0>");
- }
- AddString(&buf_p, llen, buffer, width, prec);
- arg++;
- break;
- }
- case 'N':
- {
- CHECK_ARGS(0);
- cell_t *value;
- pCtx->LocalToPhysAddr(params[arg], &value);
-
- const char *name = "Console";
- if (*value)
- {
- CPlayer *player = g_Players.GetPlayerByIndex(*value);
- if (!player || !player->IsConnected())
- {
- return pCtx->ThrowNativeError("Client index %d is invalid", *value);
- }
- name = player->GetName();
- }
- AddString(&buf_p, llen, name, width, prec);
- arg++;
- break;
- }
- case 's':
- {
- CHECK_ARGS(0);
- char *str;
- int err;
- if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
- {
- pCtx->ThrowNativeErrorEx(err, "Could not deference string");
- return 0;
- }
- AddString(&buf_p, llen, str, width, prec);
- arg++;
- break;
- }
- case 'T':
- {
- CHECK_ARGS(1);
- char *key;
- bool error;
- size_t res;
- cell_t *target;
- pCtx->LocalToString(params[arg++], &key);
- pCtx->LocalToPhysAddr(params[arg++], &target);
- res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
- if (error)
- {
- return 0;
- }
- buf_p += res;
- llen -= res;
- break;
- }
- case 't':
- {
- CHECK_ARGS(0);
- char *key;
- bool error;
- size_t res;
- cell_t target = g_SourceMod.GetGlobalTarget();
- pCtx->LocalToString(params[arg++], &key);
- res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
- if (error)
- {
- return 0;
- }
- buf_p += res;
- llen -= res;
- break;
- }
- case 'X':
- {
- CHECK_ARGS(0);
- cell_t *value;
- pCtx->LocalToPhysAddr(params[arg], &value);
- flags |= UPPERDIGITS;
- AddHex(&buf_p, llen, static_cast(*value), width, flags);
- arg++;
- break;
- }
- case 'x':
- {
- CHECK_ARGS(0);
- cell_t *value;
- pCtx->LocalToPhysAddr(params[arg], &value);
- AddHex(&buf_p, llen, static_cast(*value), width, flags);
- arg++;
- break;
- }
- case '%':
- {
- if (!llen)
- {
- goto done;
- }
- *buf_p++ = ch;
- llen--;
- break;
- }
- case '\0':
- {
- if (!llen)
- {
- goto done;
- }
- *buf_p++ = '%';
- llen--;
- goto done;
- }
- default:
- {
- if (!llen)
- {
- goto done;
- }
- *buf_p++ = ch;
- llen--;
- break;
- }
- }
- }
-
-done:
- *buf_p = '\0';
- *param = arg;
- return (maxlen - llen - 1);
-}
-
-const char *stristr(const char *str, const char *substr)
-{
- if (!*substr)
- {
- return ((char *)str);
- }
-
- char *needle = (char *)substr;
- char *prevloc = (char *)str;
- char *haystack = (char *)str;
-
- while (*haystack)
- {
- if (tolower(*haystack) == tolower(*needle))
- {
- haystack++;
- if (!*++needle)
- {
- return prevloc;
- }
- }
- else
- {
- haystack = ++prevloc;
- needle = (char *)substr;
- }
- }
-
- return NULL;
-}
-
-unsigned int strncopy(char *dest, const char *src, size_t count)
-{
- if (!count)
- {
- return 0;
- }
-
- char *start = dest;
- while ((*src) && (--count))
- {
- *dest++ = *src++;
- }
- *dest = '\0';
-
- return (dest - start);
-}
-
-size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- size_t len = vsnprintf(buffer, maxlength, fmt, ap);
- va_end(ap);
-
- if (len >= maxlength)
- {
- buffer[maxlength - 1] = '\0';
- return (maxlength - 1);
- }
- else
- {
- return len;
- }
-}
-
-size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
-{
- size_t len = vsnprintf(buffer, maxlength, fmt, ap);
-
- if (len >= maxlength)
- {
- buffer[maxlength - 1] = '\0';
- return (maxlength - 1);
- }
- else
- {
- return len;
- }
-}
-
-char *sm_strdup(const char *str)
-{
- char *ptr = new char[strlen(str)+1];
- strcpy(ptr, str);
- return ptr;
-}
-
-unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
-{
- size_t searchLen = strlen(search);
- size_t replaceLen = strlen(replace);
-
- char *ptr = subject;
- unsigned int total = 0;
- while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
- {
- total++;
- if (*ptr == '\0')
- {
- break;
- }
- }
-
- return total;
-}
-
-/**
- * NOTE: Do not edit this for the love of god unless you have
- * read the test cases and understand the code behind each one.
- * While I don't guarantee there aren't mistakes, I do guarantee
- * that plugins will end up relying on tiny idiosyncracies of this
- * function, just like they did with AMX Mod X.
- *
- * There are explicitly more cases than the AMX Mod X version because
- * we're not doing a blind copy. Each case is specifically optimized
- * for what needs to be done. Even better, we don't have to error on
- * bad buffer sizes. Instead, this function will smartly cut off the
- * string in a way that pushes old data out.
- */
-char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
-{
- char *ptr = subject;
- size_t browsed = 0;
- size_t textLen = strlen(subject);
-
- /* It's not possible to search or replace */
- if (searchLen > textLen)
- {
- return NULL;
- }
-
- /* Handle the case of one byte replacement.
- * It's only valid in one case.
- */
- if (maxLen == 1)
- {
- /* If the search matches and the replace length is 0,
- * we can just terminate the string and be done.
- */
- if (strcmp(subject, search) == 0 && replaceLen == 0)
- {
- *subject = '\0';
- return subject;
- }
- else
- {
- return NULL;
- }
- }
-
- /* Subtract one off the maxlength so we can include the null terminator */
- maxLen--;
-
- while (*ptr != '\0' && (browsed <= textLen - searchLen))
- {
- /* See if we get a comparison */
- if (strncmp(ptr, search, searchLen) == 0)
- {
- if (replaceLen > searchLen)
- {
- /* First, see if we have enough space to do this operation */
- if (maxLen - textLen < replaceLen - searchLen)
- {
- /* First, see if the replacement length goes out of bounds. */
- if (browsed + replaceLen >= maxLen)
- {
- /* EXAMPLE CASE:
- * Subject: AABBBCCC
- * Buffer : 12 bytes
- * Search : BBB
- * Replace: DDDDDDDDDD
- * OUTPUT : AADDDDDDDDD
- * POSITION: ^
- */
- /* If it does, we'll just bound the length and do a strcpy. */
- replaceLen = maxLen - browsed;
- /* Note, we add one to the final result for the null terminator */
- strncopy(ptr, replace, replaceLen+1);
- }
- else
- {
- /* EXAMPLE CASE:
- * Subject: AABBBCCC
- * Buffer : 12 bytes
- * Search : BBB
- * Replace: DDDDDDD
- * OUTPUT : AADDDDDDDCC
- * POSITION: ^
- */
- /* We're going to have some bytes left over... */
- size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
- size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
- char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
- char *moveTo = ptr + replaceLen;
-
- /* First, move our old data out of the way. */
- memmove(moveTo, moveFrom, realBytesToCopy);
-
- /* Now, do our replacement. */
- memcpy(ptr, replace, replaceLen);
- }
- }
- else
- {
- /* EXAMPLE CASE:
- * Subject: AABBBCCC
- * Buffer : 12 bytes
- * Search : BBB
- * Replace: DDDD
- * OUTPUT : AADDDDCCC
- * POSITION: ^
- */
- /* Yes, we have enough space. Do a normal move operation. */
- char *moveFrom = ptr + searchLen;
- char *moveTo = ptr + replaceLen;
-
- /* First move our old data out of the way. */
- size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
- memmove(moveTo, moveFrom, bytesToCopy);
-
- /* Now do our replacement. */
- memcpy(ptr, replace, replaceLen);
- }
- }
- else if (replaceLen < searchLen)
- {
- /* EXAMPLE CASE:
- * Subject: AABBBCCC
- * Buffer : 12 bytes
- * Search : BBB
- * Replace: D
- * OUTPUT : AADCCC
- * POSOTION: ^
- */
- /* If the replacement does not grow the string length, we do not
- * need to do any fancy checking at all. Yay!
- */
- char *moveFrom = ptr + searchLen; /* Start after the search pointer */
- char *moveTo = ptr + replaceLen; /* Copy to where the replacement ends */
-
- /* Copy our replacement in, if any */
- if (replaceLen)
- {
- memcpy(ptr, replace, replaceLen);
- }
-
- /* Figure out how many bytes to move down, including null terminator */
- size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
-
- /* Move the rest of the string down */
- memmove(moveTo, moveFrom, bytesToCopy);
- }
- else
- {
- /* EXAMPLE CASE:
- * Subject: AABBBCCC
- * Buffer : 12 bytes
- * Search : BBB
- * Replace: DDD
- * OUTPUT : AADDDCCC
- * POSITION: ^
- */
- /* We don't have to move anything around, just do a straight copy */
- memcpy(ptr, replace, replaceLen);
- }
-
- return ptr + replaceLen;
- }
- ptr++;
- browsed++;
- }
-
- return NULL;
-}