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: