3 * ===============================================================
4 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
5 * ===============================================================
7 * This file is not open source and may not be copied without explicit
8 * written permission of AlliedModders LLC. This file may not be redistributed
9 * in whole or significant part.
10 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
18 #include "sm_stringutil.h"
20 #include "PluginSys.h"
21 #include "Translator.h"
22 #include "PlayerManager.h"
24 #define LADJUST 0x00000004 /* left adjustment */
25 #define ZEROPAD 0x00000080 /* zero (as opposed to blank) pad */
26 #define UPPERDIGITS 0x00000200 /* make alpha digits uppercase */
27 #define to_digit(c) ((c) - '0')
28 #define is_digit(c) ((unsigned)to_digit(c) <= 9)
30 #define CHECK_ARGS(x) \
31 if ((arg+x) > args) { \
32 pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
36 size_t CorePlayerTranslate(int client, char *buffer, size_t maxlength, const char *phrase, void **params)
41 err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetServerLanguage(), &pTrans);
42 if (err != Trans_Okay && g_Translator.GetServerLanguage() != CORELANG_ENGLISH)
44 err = g_pCorePhrases->GetTranslation(phrase, CORELANG_ENGLISH, &pTrans);
47 if (err != Trans_Okay)
49 return UTIL_Format(buffer, maxlength, "%s", phrase);
52 return g_Translator.Translate(buffer, maxlength, params, &pTrans);
55 inline bool TryTranslation(CPlugin *pl, const char *key, unsigned int langid, unsigned int langcount, Translation *pTrans)
57 TransError err = Trans_BadLanguage;
60 for (size_t i=0; i<langcount && err!=Trans_Okay; i++)
62 phrfl = g_Translator.GetFileByIndex(pl->GetLangFileByIndex(i));
63 err = phrfl->GetTranslation(key, langid, pTrans);
66 return (err == Trans_Okay) ? true : false;
69 size_t Translate(char *buffer, size_t maxlen, IPluginContext *pCtx, const char *key, cell_t target, const cell_t *params, int *arg, bool *error)
74 CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
75 size_t langcount = pl->GetLangFileCount();
76 void *new_params[MAX_TRANSLATE_PARAMS];
77 unsigned int max_params = 0;
80 if (target == LANG_SERVER)
82 langid = g_Translator.GetServerLanguage();
83 } else if ((target >= 1) && (target <= g_Players.GetMaxClients())) {
84 langid = g_Translator.GetServerLanguage();
86 pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
90 if (!TryTranslation(pl, key, langid, langcount, &pTrans))
92 if (target != LANG_SERVER && langid != g_Translator.GetServerLanguage())
96 } else if (langid != LANGUAGE_ENGLISH) {
97 if (!TryTranslation(pl, key, LANGUAGE_ENGLISH, langcount, &pTrans))
99 pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
103 pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language prhase \"%s\" not found", key);
108 max_params = pTrans.fmt_count;
112 /* Check if we're going to over the limit */
113 if ((*arg) + (max_params - 1) > (size_t)params[0])
115 pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX,
116 "Translation string formatted incorrectly - missing at least %d parameters",
117 ((*arg + (max_params - 1)) - params[0])
122 /* Translate the parameters to raw pointers */
123 for (size_t i=0; i<max_params; i++)
125 pCtx->LocalToPhysAddr(params[*arg], reinterpret_cast<cell_t **>(&new_params[i]));
130 return g_Translator.Translate(buffer, maxlen, new_params, &pTrans);
136 //:TODO: review this code before we choose a license
138 void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
142 static char nlstr[] = {'(','n','u','l','l',')','\0'};
154 for (size = 0; size < prec; size++)
156 if (string[size] == '\0')
162 while (string[size++]);
166 if (size > (int)maxlen)
179 while ((width-- > 0) && maxlen)
188 void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec)
203 // write the float number
208 text[digits++] = '0' + val % 10;
214 text[digits++] = '-';
219 while ((digits < width) && maxlen)
226 while ((digits--) && maxlen)
228 *buf++ = text[digits];
238 // write the fraction
240 while (digits < prec)
245 text[digits++] = '0' + val % 10;
248 if ((digits > 0) && maxlen)
253 for (prec = 0; maxlen && (prec < digits); prec++)
262 void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
271 text[digits++] = '0' + val % 10;
277 if (!(flags & LADJUST))
279 while (digits < width && maxlen)
281 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
287 while (digits-- && maxlen)
289 *buf++ = text[digits];
296 while (width-- && maxlen)
298 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
306 void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
312 unsigned int unsignedVal;
318 /* we want the unsigned version */
319 unsignedVal = abs(val);
325 text[digits++] = '0' + unsignedVal % 10;
327 } while (unsignedVal);
331 text[digits++] = '-';
336 if (!(flags & LADJUST))
338 while ((digits < width) && maxlen)
340 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
346 while (digits-- && maxlen)
348 *buf++ = text[digits];
355 while (width-- && maxlen)
357 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
365 void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
373 if (flags & UPPERDIGITS)
375 hexadjust = 'A' - '9' - 1;
377 hexadjust = 'a' - '9' - 1;
383 digit = ('0' + val % 16);
389 text[digits++] = digit;
395 if (!(flags & LADJUST))
397 while (digits < width && maxlen)
399 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
405 while (digits-- && maxlen)
407 *buf++ = text[digits];
414 while (width-- && maxlen)
416 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
424 size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
426 if (!buffer || !maxlen)
440 size_t llen = maxlen - 1;
447 // run through the format string until we hit a '%' or '\0'
448 for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
453 if ((ch == '\0') || (llen <= 0))
461 // reset formatting state
480 while(is_digit((ch = *fmt++)))
482 n = 10 * n + (ch - '0');
484 prec = (n < 0) ? -1 : n;
505 n = 10 * n + (ch - '0');
507 } while(is_digit(ch));
517 char *c = (char *)args[arg];
526 int *value = (int *)args[arg];
527 AddInt(&buf_p, llen, *value, width, flags);
533 unsigned int *value = (unsigned int *)args[arg];
534 AddUInt(&buf_p, llen, *value, width, flags);
540 float *value = (float *)args[arg];
541 AddFloat(&buf_p, llen, *value, width, prec);
547 const char *str = (const char *)args[arg];
548 AddString(&buf_p, llen, str, width, prec);
554 unsigned int *value = (unsigned int *)args[arg];
555 flags |= UPPERDIGITS;
556 AddHex(&buf_p, llen, *value, width, flags);
562 unsigned int *value = (unsigned int *)args[arg];
563 AddHex(&buf_p, llen, *value, width, flags);
603 return (maxlen - llen - 1);
606 size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
608 if (!buffer || !maxlen)
614 int args = params[0];
623 size_t llen = maxlen - 1;
631 // run through the format string until we hit a '%' or '\0'
632 for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
637 if ((ch == '\0') || (llen <= 0))
645 // reset formatting state
664 while(is_digit((ch = *fmt++)))
666 n = 10 * n + (ch - '0');
668 prec = (n < 0) ? -1 : n;
689 n = 10 * n + (ch - '0');
691 } while(is_digit(ch));
703 pCtx->LocalToString(params[arg], &c);
714 pCtx->LocalToPhysAddr(params[arg], &value);
715 AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
723 pCtx->LocalToPhysAddr(params[arg], &value);
724 AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
732 pCtx->LocalToPhysAddr(params[arg], &value);
733 AddFloat(&buf_p, llen, sp_ctof(*value), width, prec);
741 pCtx->LocalToPhysAddr(params[arg], &value);
745 CPlayer *player = g_Players.GetPlayerByIndex(*value);
746 if (!player || !player->IsConnected())
748 return pCtx->ThrowNativeError("Client index %d is invalid", *value);
750 const char *auth = player->GetAuthString();
751 if (!auth || auth[0] == '\0')
753 auth = "STEAM_ID_PENDING";
755 int userid = engine->GetPlayerUserId(player->GetEdict());
765 "Console<0><Console><Console>");
767 AddString(&buf_p, llen, buffer, width, prec);
776 if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
778 pCtx->ThrowNativeErrorEx(err, "Could not deference string");
781 AddString(&buf_p, llen, str, width, prec);
792 pCtx->LocalToString(params[arg++], &key);
793 pCtx->LocalToPhysAddr(params[arg++], &target);
794 res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
809 cell_t target = g_SourceMod.GetGlobalTarget();
810 pCtx->LocalToString(params[arg++], &key);
811 res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
824 pCtx->LocalToPhysAddr(params[arg], &value);
825 flags |= UPPERDIGITS;
826 AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
834 pCtx->LocalToPhysAddr(params[arg], &value);
835 AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
875 return (maxlen - llen - 1);
878 const char *stristr(const char *str, const char *substr)
882 return ((char *)str);
885 char *needle = (char *)substr;
886 char *prevloc = (char *)str;
887 char *haystack = (char *)str;
891 if (tolower(*haystack) == tolower(*needle))
899 haystack = ++prevloc;
900 needle = (char *)substr;
907 unsigned int strncopy(char *dest, const char *src, size_t count)
915 while ((*src) && (--count))
921 return (dest - start);
924 size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
928 size_t len = vsnprintf(buffer, maxlength, fmt, ap);
931 if (len >= maxlength)
933 buffer[maxlength - 1] = '\0';
934 return (maxlength - 1);
940 size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
942 size_t len = vsnprintf(buffer, maxlength, fmt, ap);
944 if (len >= maxlength)
946 buffer[maxlength - 1] = '\0';
947 return (maxlength - 1);
953 char *sm_strdup(const char *str)
955 char *ptr = new char[strlen(str)+1];
960 unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
962 size_t searchLen = strlen(search);
963 size_t replaceLen = strlen(replace);
966 unsigned int total = 0;
967 while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
980 * NOTE: Do not edit this for the love of god unless you have
981 * read the test cases and understand the code behind each one.
982 * While I don't guarantee there aren't mistakes, I do guarantee
983 * that plugins will end up relying on tiny idiosyncracies of this
984 * function, just like they did with AMX Mod X.
986 * There are explicitly more cases than the AMX Mod X version because
987 * we're not doing a blind copy. Each case is specifically optimized
988 * for what needs to be done. Even better, we don't have to error on
989 * bad buffer sizes. Instead, this function will smartly cut off the
990 * string in a way that pushes old data out.
992 char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
996 size_t textLen = strlen(subject);
998 /* It's not possible to search or replace */
999 if (searchLen > textLen || maxLen <= 1)
1004 /* Subtract one off the maxlength so we can include the null terminator */
1007 while (*ptr != '\0' && (browsed <= textLen - searchLen))
1009 /* See if we get a comparison */
1010 if (strncmp(ptr, search, searchLen) == 0)
1012 if (replaceLen > searchLen)
1014 /* First, see if we have enough space to do this operation */
1015 if (maxLen - textLen < replaceLen - searchLen)
1017 /* First, see if the replacement length goes out of bounds. */
1018 if (browsed + replaceLen >= maxLen)
1024 * Replace: DDDDDDDDDD
1025 * OUTPUT : AADDDDDDDDD
1028 /* If it does, we'll just bound the length and do a strcpy. */
1029 replaceLen = maxLen - browsed;
1030 /* Note, we add one to the final result for the null terminator */
1031 strncopy(ptr, replace, replaceLen+1);
1038 * OUTPUT : AADDDDDDDCC
1041 /* We're going to have some bytes left over... */
1042 size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
1043 size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
1044 char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
1045 char *moveTo = ptr + replaceLen;
1047 /* First, move our old data out of the way. */
1048 memmove(moveTo, moveFrom, realBytesToCopy);
1050 /* Now, do our replacement. */
1051 memcpy(ptr, replace, replaceLen);
1059 * OUTPUT : AADDDDCCC
1062 /* Yes, we have enough space. Do a normal move operation. */
1063 char *moveFrom = ptr + searchLen;
1064 char *moveTo = ptr + replaceLen;
1066 /* First move our old data out of the way. */
1067 size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
1068 memmove(moveTo, moveFrom, bytesToCopy);
1070 /* Now do our replacement. */
1071 memcpy(ptr, replace, replaceLen);
1073 } else if (replaceLen < searchLen) {
1082 /* If the replacement does not grow the string length, we do not
1083 * need to do any fancy checking at all. Yay!
1085 char *moveFrom = ptr + searchLen; /* Start after the search pointer */
1086 char *moveTo = ptr + replaceLen; /* Copy to where the replacement ends */
1088 /* Copy our replacement in, if any */
1091 memcpy(ptr, replace, replaceLen);
1094 /* Figure out how many bytes to move down, including null terminator */
1095 size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
1097 /* Move the rest of the string down */
1098 memmove(moveTo, moveFrom, bytesToCopy);
1108 /* We don't have to move anything around, just do a straight copy */
1109 memcpy(ptr, replace, replaceLen);
1112 return ptr + replaceLen;