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