Add new string natives/stocks, make some UTF-8 safe (bug 6110, r=ds)

This commit is contained in:
Arkshine
2014-04-30 09:33:03 +02:00
parent c99a518ba4
commit a86ca1491f
12 changed files with 1560 additions and 161 deletions

View File

@ -234,8 +234,9 @@ static cell AMX_NATIVE_CALL console_print(AMX *amx, cell *params) /* 2 param */
if (len > 254)
{
len = 254;
if (((message[len - 1] & 0xFF) >= 0xC2) && ((message[len - 1] & 0xFF) <= 0xEF)) { // Don't truncate a double-byte character
len--;
if ((message[len - 1] & 1 << 7))
{
len -= UTIL_CheckValidChar(message + len - 1); // Don't truncate a multi-byte character
}
}
message[len++] = '\n';
@ -252,9 +253,9 @@ static cell AMX_NATIVE_CALL console_print(AMX *amx, cell *params) /* 2 param */
if (len > 126) // Client console truncates after byte 127. (126 + \n = 127)
{
len = 126;
if (((message[len - 1] & 0xFF) >= 0xC2) && ((message[len - 1] & 0xFF) <= 0xEF)) // Don't truncate a double-byte character
if ((message[len - 1] & 1 << 7))
{
len--;
len -= UTIL_CheckValidChar(message + len - 1); // Don't truncate a multi-byte character
}
}
message[len++] = '\n'; // Client expects newline from the server
@ -289,9 +290,9 @@ static cell AMX_NATIVE_CALL client_print(AMX *amx, cell *params) /* 3 param */
if (((params[2] == 1) || (params[2] == 2)) && (len > 126)) // Client console truncates after byte 127. (126 + \n = 127)
{
len = 126;
if (((msg[len - 1] & 0xFF) >= 0xC2) && ((msg[len - 1] & 0xFF) <= 0xEF)) // Don't truncate a double-byte character
if ((msg[len - 1] & 1 << 7))
{
len--;
len -= UTIL_CheckValidChar(msg + len - 1); // Don't truncate a multi-byte character
}
}
msg[len++] = '\n'; // Client expects newline from the server
@ -323,9 +324,9 @@ static cell AMX_NATIVE_CALL client_print(AMX *amx, cell *params) /* 3 param */
if (((params[2] == 1) || (params[2] == 2)) && (len > 126)) // Client console truncates after byte 127. (126 + \n = 127)
{
len = 126;
if (((msg[len - 1] & 0xFF) >= 0xC2) && ((msg[len - 1] & 0xFF) <= 0xEF)) // Don't truncate a double-byte character
if ((msg[len - 1] & 1 << 7))
{
len--;
len -= UTIL_CheckValidChar(msg + len - 1); // Don't truncate a multi-byte character
}
}
msg[len++] = '\n'; // Client expects newline from the server
@ -370,6 +371,16 @@ static cell AMX_NATIVE_CALL client_print_color(AMX *amx, cell *params) /* 3 para
{
g_langMngr.SetDefLang(i);
msg = format_amxstring(amx, params, 3, len);
if (len > 190) // Server crashes after byte 190. (190 + \n = 191)
{
len = 190;
if ((msg[len - 1] & 1 << 7))
{
len -= UTIL_CheckValidChar(msg + len - 1); // Don't truncate a multi-byte character
}
}
msg[len++] = '\n';
msg[len] = 0;
@ -392,6 +403,16 @@ static cell AMX_NATIVE_CALL client_print_color(AMX *amx, cell *params) /* 3 para
g_langMngr.SetDefLang(index);
msg = format_amxstring(amx, params, 3, len);
if (len > 190) // Server crashes after byte 190. (190 + \n = 191)
{
len = 190;
if ((msg[len - 1] & 1 << 7))
{
len -= UTIL_CheckValidChar(msg + len - 1); // Don't truncate a multi-byte character
}
}
msg[len++] = '\n';
msg[len] = 0;
@ -663,7 +684,9 @@ static cell AMX_NATIVE_CALL get_user_name(AMX *amx, cell *params) /* 3 param */
{
int index = params[1];
return set_amxstring(amx, params[2], (index < 1 || index > gpGlobals->maxClients) ? hostname->string : g_players[index].name.c_str(), params[3]);
return set_amxstring_utf8(amx, params[2], (index < 1 || index > gpGlobals->maxClients) ?
hostname->string :
g_players[index].name.c_str(), g_players[index].name.size(), params[3] + 1);
}
static cell AMX_NATIVE_CALL get_user_index(AMX *amx, cell *params) /* 1 param */
@ -1597,8 +1620,8 @@ static cell AMX_NATIVE_CALL get_concmd(AMX *amx, cell *params) /* 7 param */
if (cmd == 0)
return 0;
set_amxstring(amx, params[2], cmd->getCmdLine(), params[3]);
set_amxstring(amx, params[5], cmd->getCmdInfo(), params[6]);
set_amxstring_utf8(amx, params[2], cmd->getCmdLine(), strlen(cmd->getCmdLine()), params[3] + 1); // + EOS
set_amxstring_utf8(amx, params[5], cmd->getCmdInfo(), strlen(cmd->getCmdInfo()), params[6] + 1); // + EOS
cell *cpFlags = get_amxaddr(amx, params[4]);
*cpFlags = cmd->getFlags();
@ -1633,9 +1656,9 @@ static cell AMX_NATIVE_CALL get_clcmd(AMX *amx, cell *params) /* 7 param */
if (cmd == 0)
return 0;
set_amxstring(amx, params[2], cmd->getCmdLine(), params[3]);
set_amxstring(amx, params[5], cmd->getCmdInfo(), params[6]);
set_amxstring_utf8(amx, params[2], cmd->getCmdLine(), strlen(cmd->getCmdLine()), params[3] + 1); // + EOS
set_amxstring_utf8(amx, params[5], cmd->getCmdInfo(), strlen(cmd->getCmdInfo()), params[6] + 1); // + EOS
cell *cpFlags = get_amxaddr(amx, params[4]);
*cpFlags = cmd->getFlags();
@ -1649,8 +1672,8 @@ static cell AMX_NATIVE_CALL get_srvcmd(AMX *amx, cell *params)
if (cmd == 0)
return 0;
set_amxstring(amx, params[2], cmd->getCmdLine(), params[3]);
set_amxstring(amx, params[5], cmd->getCmdInfo(), params[6]);
set_amxstring_utf8(amx, params[2], cmd->getCmdLine(), strlen(cmd->getCmdLine()), params[3] + 1); // + EOS
set_amxstring_utf8(amx, params[5], cmd->getCmdInfo(), strlen(cmd->getCmdInfo()), params[6] + 1); // + EOS
cell *cpFlags = get_amxaddr(amx, params[4]);
*cpFlags = cmd->getFlags();
@ -1891,7 +1914,7 @@ static cell AMX_NATIVE_CALL get_pcvar_string(AMX *amx, cell *params)
return 0;
}
return set_amxstring(amx, params[2], ptr->string ? ptr->string : "", params[3]);
return set_amxstring_utf8(amx, params[2], ptr->string ? ptr->string : "", ptr->string ? strlen(ptr->string) : 0, params[3] + 1); // EOS
}
static cell AMX_NATIVE_CALL get_cvar_string(AMX *amx, cell *params) /* 3 param */
@ -1912,7 +1935,8 @@ static cell AMX_NATIVE_CALL get_cvar_string(AMX *amx, cell *params) /* 3 param *
}
}
return set_amxstring(amx, params[2], CVAR_GET_STRING(sptemp), params[3]);
const char *value = CVAR_GET_STRING(sptemp);
return set_amxstring_utf8(amx, params[2], value, strlen(value), params[3] + 1); // + EOS
}
static cell AMX_NATIVE_CALL get_pcvar_float(AMX *amx, cell *params)
@ -2168,9 +2192,9 @@ static cell AMX_NATIVE_CALL format_time(AMX *amx, cell *params) /* 3 param */
}
char szDate[512];
strftime(szDate, 511, sptemp, lt);
ilen = strftime(szDate, 511, sptemp, lt); // Returns length, including null-character.
return set_amxstring(amx, params[1], szDate, params[2]);
return set_amxstring_utf8(amx, params[1], szDate, ilen - 1, params[2] + 1); // + EOS
}
@ -2235,7 +2259,8 @@ static cell AMX_NATIVE_CALL read_data(AMX *amx, cell *params) /* 3 param */
case 1:
return g_events.getArgInteger(params[1]);
case 3:
return set_amxstring(amx, params[2], g_events.getArgString(params[1]), *get_amxaddr(amx, params[3]));
return set_amxstring_utf8(amx, params[2], g_events.getArgString(params[1]),
strlen(g_events.getArgString(params[1])),*get_amxaddr(amx, params[3]) + 1); // + EOS
default:
cell *fCell = get_amxaddr(amx, params[2]);
REAL fparam = (REAL)g_events.getArgFloat(params[1]);
@ -2438,7 +2463,8 @@ static cell AMX_NATIVE_CALL get_localinfo(AMX *amx, cell *params) /* 3 param */
int ilen;
char* sptemp = get_amxstring(amx, params[1], 0, ilen);
return set_amxstring(amx, params[2], LOCALINFO(sptemp), params[3]);
char *value = LOCALINFO(sptemp);
return set_amxstring_utf8(amx, params[2], value, strlen(value), params[3] + 1); // + EOS
}
static cell AMX_NATIVE_CALL set_localinfo(AMX *amx, cell *params) /* 2 param */
@ -2511,14 +2537,15 @@ static cell AMX_NATIVE_CALL read_argc(AMX *amx, cell *params)
static cell AMX_NATIVE_CALL read_argv(AMX *amx, cell *params) /* 3 param */
{
return set_amxstring(amx, params[2], /*(params[1] < 0 ||
params[1] >= CMD_ARGC()) ? "" : */CMD_ARGV(params[1]), params[3]);
const char *value = CMD_ARGV(params[1]);
return set_amxstring_utf8(amx, params[2], /*(params[1] < 0 ||
params[1] >= CMD_ARGC()) ? "" : */value, strlen(value), params[3] + 1); // + EOS
}
static cell AMX_NATIVE_CALL read_args(AMX *amx, cell *params) /* 2 param */
{
const char* sValue = CMD_ARGS();
return set_amxstring(amx, params[1], sValue ? sValue : "", params[2]);
return set_amxstring_utf8(amx, params[1], sValue ? sValue : "", sValue ? strlen(sValue) : 0, params[2] + 1); // +EOS
}
static cell AMX_NATIVE_CALL get_user_msgid(AMX *amx, cell *params) /* 1 param */
@ -3210,7 +3237,8 @@ static cell AMX_NATIVE_CALL force_unmodified(AMX *amx, cell *params)
static cell AMX_NATIVE_CALL read_logdata(AMX *amx, cell *params)
{
return set_amxstring(amx, params[1], g_logevents.getLogString(), params[2]);
const char *value = g_logevents.getLogString();
return set_amxstring_utf8(amx, params[1], value, strlen(value), params[2] + 1); // + EOS
}
static cell AMX_NATIVE_CALL read_logargc(AMX *amx, cell *params)
@ -3220,7 +3248,8 @@ static cell AMX_NATIVE_CALL read_logargc(AMX *amx, cell *params)
static cell AMX_NATIVE_CALL read_logargv(AMX *amx, cell *params)
{
return set_amxstring(amx, params[2], g_logevents.getLogArg(params[1]), params[3]);
const char *value = g_logevents.getLogArg(params[1]);
return set_amxstring_utf8(amx, params[2], value, strlen(value), params[3] + 1); // + EOS
}
static cell AMX_NATIVE_CALL parse_loguser(AMX *amx, cell *params)
@ -3503,10 +3532,14 @@ static cell AMX_NATIVE_CALL get_module(AMX *amx, cell *params)
// set name, author, version
if ((*moduleIter).isAmxx())
{
const amxx_module_info_s *info = (*moduleIter).getInfoNew();
set_amxstring(amx, params[2], info && info->name ? info->name : "unk", params[3]);
set_amxstring(amx, params[4], info && info->author ? info->author : "unk", params[5]);
set_amxstring(amx, params[6], info && info->version ? info->version : "unk", params[7]);
const amxx_module_info_s *info = (*moduleIter).getInfoNew();
const char *name = info && info->name ? info->name : "unk";
const char *author = info && info->author ? info->author : "unk";
const char *version = info && info->version ? info->version : "unk";
set_amxstring_utf8(amx, params[2], name, strlen(name), params[3] + 1); // + EOS
set_amxstring_utf8(amx, params[4], author, strlen(author), params[5] + 1); // + EOS
set_amxstring_utf8(amx, params[6], version, strlen(version), params[7] + 1); // + EOS
}
// compatibility problem possible

View File

@ -147,6 +147,11 @@ void UTIL_ShowMenu(edict_t* pEntity, int slots, int time, char *menu, int mlen);
void UTIL_ClientSayText(edict_t *pEntity, int sender, char *msg);
void UTIL_TeamInfo(edict_t *pEntity, int playerIndex, const char *pszTeamName);
template <typename D>
int UTIL_CheckValidChar(D *c);
unsigned int UTIL_GetUTF8CharBytes(const char *stream);
unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace, bool caseSensitive);
char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen, bool caseSensitive);
char *UTIL_VarArgs(const char *fmt, ...);
@ -294,6 +299,7 @@ int amxstring_len(cell* cstr);
int load_amxscript(AMX* amx, void** program, const char* path, char error[64], int debug);
int set_amxnatives(AMX* amx, char error[64]);
int set_amxstring(AMX *amx, cell amx_addr, const char *source, int max);
int set_amxstring_utf8(AMX *amx, cell amx_addr, const char *source, size_t sourcelen, size_t maxlen);
int unload_amxscript(AMX* amx, void** program);
void copy_amxmemory(cell* dest, cell* src, int len);

View File

@ -137,13 +137,14 @@ static cell AMX_NATIVE_CALL ReadPackString(AMX* amx, cell* params)
}
const char *str;
if (!(str = d->ReadString(NULL)))
size_t len;
if (!(str = d->ReadString(&len)))
{
LogError(amx, AMX_ERR_NATIVE, "DataPack operation is out of bounds.");
return 0;
}
return set_amxstring(amx, params[2], str, params[3]);
return set_amxstring_utf8(amx, params[2], str, len, params[3] + 1); // + EOS
}
static cell AMX_NATIVE_CALL ResetPack(AMX* amx, cell* params)

View File

@ -163,6 +163,13 @@ public:
*out='\0';
}
/* Don't truncate a multi-byte character */
if (*(output - 1) & 1 << 7)
{
size = UTIL_CheckValidChar(output - 1);
*(output - size) = '\0';
}
return 1;
}
// Returns 1 on success

View File

@ -169,7 +169,7 @@ static cell AMX_NATIVE_CALL read_file(AMX *amx, cell *params) /* 5 param */
buffor[--len] = 0;
cell *length = get_amxaddr(amx, params[5]);
*length = set_amxstring(amx, params[3], buffor, params[4]);
*length = set_amxstring_utf8(amx, params[3], buffor, len, params[4] + 1); // + EOS
return i;
}
@ -583,7 +583,7 @@ static cell AMX_NATIVE_CALL amx_fgets(AMX *amx, cell *params)
static char buffer[4096];
buffer[0] = '\0';
fgets(buffer, sizeof(buffer)-1, fp);
return set_amxstring(amx, params[2], buffer, params[3]);
return set_amxstring_utf8(amx, params[2], buffer, strlen(buffer), params[3] + 1); // + EOS
}
static cell AMX_NATIVE_CALL amx_fseek(AMX *amx, cell *params)

View File

@ -149,6 +149,12 @@ void AddString(U **buf_p, size_t &maxlen, const cell *string, int width, int pre
if (size > (int)maxlen)
size = maxlen;
/* If precision is provided, make sure we don't truncate a multi-byte character */
if (prec >= size && (string[size - 1] & 1 << 7))
{
size -= UTIL_CheckValidChar((cell *)string + size - 1);
}
maxlen -= size;
width -= size;
@ -286,6 +292,58 @@ void AddFloat(U **buf_p, size_t &maxlen, double fval, int width, int prec, int f
*buf_p = buf;
}
template <typename U>
void AddBinary(U **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
{
char text[32];
int digits;
U *buf;
digits = 0;
do
{
if (val & 1)
{
text[digits++] = '1';
}
else
{
text[digits++] = '0';
}
val >>= 1;
} while (val);
buf = *buf_p;
if (!(flags & LADJUST))
{
while (digits < width && maxlen)
{
*buf++ = (flags & ZEROPAD) ? '0' : ' ';
width--;
maxlen--;
}
}
while (digits-- && maxlen)
{
*buf++ = text[digits];
width--;
maxlen--;
}
if (flags & LADJUST)
{
while (width-- && maxlen)
{
*buf++ = (flags & ZEROPAD) ? '0' : ' ';
maxlen--;
}
}
*buf_p = buf;
}
template <typename U>
void AddUInt(U **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
{
@ -527,6 +585,11 @@ reswitch:
llen--;
arg++;
break;
case 'b':
CHECK_ARGS(0);
AddBinary(&buf_p, llen, *get_amxaddr(amx, params[arg]), width, flags);
arg++;
break;
case 'd':
case 'i':
CHECK_ARGS(0);
@ -635,6 +698,14 @@ break_to_normal_string:
done:
*buf_p = static_cast<D>(0);
*param = arg;
/* if max buffer length consumed, make sure we don't truncate a multi-byte character */
if (llen <= 0 && *(buf_p - 1) & 1 << 7)
{
llen += UTIL_CheckValidChar(buf_p - 1);
*(buf_p - llen) = static_cast<D>(0);
}
return maxlen-llen;
}

View File

@ -103,13 +103,44 @@ int set_amxstring(AMX *amx, cell amx_addr, const char *source, int max)
#endif
while (max-- && *source)
*dest++ = (cell)*source++;
*dest++ = (unsigned char)*source++;
*dest = 0;
return dest - start;
}
int set_amxstring_utf8(AMX *amx, cell amx_addr, const char *source, size_t sourcelen, size_t maxlen)
{
size_t len = sourcelen;
bool needtocheck = false;
register cell* dest = (cell *)(amx->base + (int)(((AMX_HEADER *)amx->base)->dat + amx_addr));
register cell* start = dest;
if (len >= maxlen)
{
len = maxlen - 1;
needtocheck = true;
}
maxlen = len;
while (maxlen-- && *source)
{
*dest++ = *(unsigned char*)source++;
}
if (needtocheck && (start[len - 1] & 1 << 7))
{
len -= UTIL_CheckValidChar(start + len - 1);
}
start[len] = '\0';
return len;
}
extern "C" size_t get_amxstring_r(AMX *amx, cell amx_addr, char *destination, int maxlen)
{
register cell *source = (cell *)(amx->base + (int)(((AMX_HEADER *)amx->base)->dat + amx_addr));
@ -287,6 +318,62 @@ static cell AMX_NATIVE_CALL replace(AMX *amx, cell *params) /* 4 param */
return 0;
}
static cell AMX_NATIVE_CALL replace_string(AMX *amx, cell *params)
{
int len;
size_t maxlength = (size_t)params[2];
char *text = get_amxstring(amx, params[1], 0, len);
const char *search = get_amxstring(amx, params[3], 1, len);
const char *replace = get_amxstring(amx, params[4], 2, len);
bool caseSensitive = params[5] ? true : false;
if (search[0] == '\0')
{
LogError(amx, AMX_ERR_NATIVE, "Cannot replace searches of empty strings.");
return -1;
}
int count = UTIL_ReplaceAll(text, maxlength + 1, search, replace, caseSensitive); // + EOS
set_amxstring(amx, params[1], text, maxlength);
return count;
}
static cell AMX_NATIVE_CALL replace_stringex(AMX *amx, cell *params)
{
int len;
size_t maxlength = (size_t)params[2];
char *text = get_amxstring(amx, params[1], 0, len);
const char *search = get_amxstring(amx, params[3], 1, len);
const char *replace = get_amxstring(amx, params[4], 2, len);
size_t searchLen = (params[5] == -1) ? strlen(search) : (size_t)params[5];
size_t replaceLen = (params[6] == -1) ? strlen(replace) : (size_t)params[6];
bool caseSensitive = params[7] ? true : false;
if (searchLen == 0)
{
LogError(amx, AMX_ERR_NATIVE, "Cannot replace searches of empty strings.");
return -1;
}
char *ptr = UTIL_ReplaceEx(text, maxlength + 1, search, searchLen, replace, replaceLen, caseSensitive); // + EOS
if (ptr == NULL)
{
return -1;
}
set_amxstring(amx, params[1], ptr, maxlength);
return ptr - text;
}
static cell AMX_NATIVE_CALL contain(AMX *amx, cell *params) /* 2 param */
{
register cell *a = get_amxaddr(amx, params[2]);
@ -854,8 +941,8 @@ static cell AMX_NATIVE_CALL amx_strtok(AMX *amx, cell *params)
right[right_pos] = 0;
left[left_pos] = 0;
set_amxstring(amx, params[2], left, leftMax);
set_amxstring(amx, params[4], right, rightMax);
set_amxstring_utf8(amx, params[2], left, strlen(left), leftMax + 1); // +EOS
set_amxstring_utf8(amx, params[4], right, strlen(right), rightMax + 1); // +EOS
delete [] left;
delete [] right;
@ -928,8 +1015,9 @@ static cell AMX_NATIVE_CALL amx_strtok2(AMX *amx, cell *params)
right[right_pos] = 0;
left[left_pos] = 0;
set_amxstring(amx, params[2], left, left_max);
set_amxstring(amx, params[4], right, right_max);
set_amxstring_utf8(amx, params[2], left, strlen(left), left_max + 1); // + EOS
set_amxstring_utf8(amx, params[4], right, strlen(right), right_max + 1); // + EOS
delete [] left;
delete [] right;
@ -1029,7 +1117,7 @@ do_copy:
: end - beg
)
: 0;
set_amxstring(amx, params[2], start, copylen);
set_amxstring_utf8(amx, params[2], start, strlen(start), copylen + 1); // + EOS
end = (len-i+1 > (size_t)RightMax) ? (size_t)RightMax : len-i+1;
if (end)
@ -1045,13 +1133,50 @@ do_copy:
}
//if we got here, there was nothing to break
set_amxstring(amx, params[2], &(string[beg]), LeftMax);
set_amxstring_utf8(amx, params[2], &(string[beg]), strlen(&(string[beg])), LeftMax + 1); // + EOS
if (RightMax)
*right = '\0';
return 1;
}
static cell AMX_NATIVE_CALL split_string(AMX *amx, cell *params)
{
int textLen, splitLen;
char *text = get_amxstring(amx, params[1], 0, textLen);
const char *split = get_amxstring(amx, params[2], 1, splitLen);
if (splitLen > textLen)
{
return -1;
}
int maxLen = params[4];
/**
* Note that it's <= ... you could also just add 1,
* but this is a bit nicer
*/
for (int i = 0; i <= textLen - splitLen; i++)
{
if (strncmp(&text[i], split, splitLen) == 0)
{
/* Split hereeeee */
if (i >= maxLen + 1) // + null terminator
{
set_amxstring_utf8(amx, params[3], text, textLen, maxLen + 1); // + null terminator
}
else
{
set_amxstring_utf8(amx, params[3], text, textLen, i + 1);
}
return i + splitLen;
}
}
return -1;
}
static cell AMX_NATIVE_CALL format_args(AMX *amx, cell *params)
{
int len;
@ -1065,29 +1190,102 @@ static cell AMX_NATIVE_CALL format_args(AMX *amx, cell *params)
char* string = format_arguments(amx, pos, len); // indexed from 0
return set_amxstring(amx, params[1], string, params[2]);
return set_amxstring_utf8(amx, params[1], string, len, params[2] + 1); // + EOS
}
static cell AMX_NATIVE_CALL is_digit(AMX *amx, cell *params)
{
return isdigit(params[1]);
char chr = params[1];
if (UTIL_GetUTF8CharBytes(&chr) != 1)
{
return 0;
}
return isdigit(chr);
}
static cell AMX_NATIVE_CALL is_alnum(AMX *amx, cell *params)
{
return isalnum(params[1]);
char chr = params[1];
if (UTIL_GetUTF8CharBytes(&chr) != 1)
{
return 0;
}
return isalnum(chr);
}
static cell AMX_NATIVE_CALL is_space(AMX *amx, cell *params)
{
return isspace(params[1]);
char chr = params[1];
if (UTIL_GetUTF8CharBytes(&chr) != 1)
{
return 0;
}
return isspace(chr);
}
static cell AMX_NATIVE_CALL is_alpha(AMX *amx, cell *params)
{
return isalpha(params[1]);
char chr = params[1];
if (UTIL_GetUTF8CharBytes(&chr) != 1)
{
return 0;
}
return isalpha(chr);
}
static cell AMX_NATIVE_CALL is_char_upper(AMX *amx, cell *params)
{
char chr = params[1];
if (UTIL_GetUTF8CharBytes(&chr) != 1)
{
return 0;
}
return isupper(chr);
}
static cell AMX_NATIVE_CALL is_char_lower(AMX *amx, cell *params)
{
char chr = params[1];
if (UTIL_GetUTF8CharBytes(&chr) != 1)
{
return 0;
}
return islower(chr);
}
static cell AMX_NATIVE_CALL is_char_mb(AMX *amx, cell *params)
{
char chr = params[1];
unsigned int bytes = UTIL_GetUTF8CharBytes(&chr);
if (bytes == 1)
{
return 0;
}
return bytes;
}
static cell AMX_NATIVE_CALL get_char_bytes(AMX *amx, cell *params)
{
int len;
char *str = get_amxstring(amx, params[1], 0, len);
return UTIL_GetUTF8CharBytes(str);
};
static cell AMX_NATIVE_CALL amx_ucfirst(AMX *amx, cell *params)
{
cell *str = get_amxaddr(amx, params[1]);
@ -1163,6 +1361,18 @@ static cell AMX_NATIVE_CALL n_strcmp(AMX *amx, cell *params)
return strcmp(str1, str2);
}
static cell AMX_NATIVE_CALL n_strncmp(AMX *amx, cell *params)
{
int len;
char *str1 = get_amxstring(amx, params[1], 0, len);
char *str2 = get_amxstring(amx, params[2], 1, len);
if (params[4])
return strncmp(str1, str2, (size_t)params[3]);
else
return strncasecmp(str1, str2, (size_t)params[3]);
}
static cell AMX_NATIVE_CALL n_strfind(AMX *amx, cell *params)
{
int len;
@ -1274,13 +1484,20 @@ AMX_NATIVE_INFO string_Natives[] =
{"isalnum", is_alnum},
{"isspace", is_space},
{"isalpha", is_alpha},
{"is_char_upper", is_char_upper},
{"is_char_lower", is_char_lower},
{"is_char_mb", is_char_mb},
{"get_char_bytes", get_char_bytes},
{"num_to_str", numtostr},
{"numtostr", numtostr},
{"parse", parse},
{"replace", replace},
{"replace_string", replace_string},
{"replace_stringex",replace_stringex},
{"setc", setc},
{"strbreak", strbreak},
{"argparse", argparse},
{"split_string", split_string},
{"strtolower", strtolower},
{"strtoupper", strtoupper},
{"str_to_num", strtonum},
@ -1295,6 +1512,7 @@ AMX_NATIVE_INFO string_Natives[] =
{"strcat", n_strcat},
{"strfind", n_strfind},
{"strcmp", n_strcmp},
{"strncmp", n_strncmp},
{"str_to_float", str_to_float},
{"float_to_str", float_to_str},
{"vformat", vformat},

View File

@ -112,7 +112,16 @@ public:
{
if (m_type == TRIE_DATA_STRING && max >= 0)
{
memcpy(out, m_data, (max > m_cellcount ? m_cellcount : max) * sizeof(cell));
int len = (max > m_cellcount) ? m_cellcount : max;
memcpy(out, m_data, len * sizeof(cell));
/* Don't truncate a multi-byte character */
if (m_data[len - 1] & 1 << 7)
{
len -= UTIL_CheckValidChar(m_data + len - 1);
out[len] = '\0';
}
return true;
}
return false;

View File

@ -397,3 +397,275 @@ void UTIL_FakeClientCommand(edict_t *pEdict, const char *cmd, const char *arg1,
g_fakecmd.fake = false;
}
unsigned int UTIL_GetUTF8CharBytes(const char *stream)
{
unsigned char c = *(unsigned char *)stream;
if (c & (1 << 7))
{
if (c & (1 << 5))
{
if (c & (1 << 4))
{
return 4;
}
return 3;
}
return 2;
}
return 1;
}
template int UTIL_CheckValidChar<char>(char *);
template int UTIL_CheckValidChar<cell>(cell *);
template <typename D>
int UTIL_CheckValidChar(D *c)
{
int count;
int bytecount = 0;
for (count = 1; (*c & 0xC0) == 0x80; count++)
{
c--;
}
switch (*c & 0xF0)
{
case 0xC0:
case 0xD0:
{
bytecount = 2;
break;
}
case 0xE0:
{
bytecount = 3;
break;
}
case 0xF0:
{
bytecount = 4;
break;
}
}
if (bytecount != count)
{
return count;
}
return 0;
}
unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace, bool caseSensitive)
{
size_t searchLen = strlen(search);
size_t replaceLen = strlen(replace);
char *ptr = subject;
unsigned int total = 0;
while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen, caseSensitive)) != NULL)
{
total++;
if (*ptr == '\0')
{
break;
}
}
return total;
}
unsigned int strncopy(char *dest, const char *src, size_t count)
{
if (!count)
{
return 0;
}
char *start = dest;
while ((*src) && (--count))
{
*dest++ = *src++;
}
*dest = '\0';
return (dest - start);
}
/**
* NOTE: Do not edit this for the love of god unless you have
* read the test cases and understand the code behind each one.
* While I don't guarantee there aren't mistakes, I do guarantee
* that plugins will end up relying on tiny idiosyncrasies of this
* function, just like they did with AMX Mod X.
*
* There are explicitly more cases than the AMX Mod X version because
* we're not doing a blind copy. Each case is specifically optimized
* for what needs to be done. Even better, we don't have to error on
* bad buffer sizes. Instead, this function will smartly cut off the
* string in a way that pushes old data out.
*/
char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen, bool caseSensitive)
{
char *ptr = subject;
size_t browsed = 0;
size_t textLen = strlen(subject);
/* It's not possible to search or replace */
if (searchLen > textLen)
{
return NULL;
}
/* Handle the case of one byte replacement.
* It's only valid in one case.
*/
if (maxLen == 1)
{
/* If the search matches and the replace length is 0,
* we can just terminate the string and be done.
*/
if ((caseSensitive ? strcmp(subject, search) : strcasecmp(subject, search)) == 0 && replaceLen == 0)
{
*subject = '\0';
return subject;
}
else
{
return NULL;
}
}
/* Subtract one off the maxlength so we can include the null terminator */
maxLen--;
while (*ptr != '\0' && (browsed <= textLen - searchLen))
{
/* See if we get a comparison */
if ((caseSensitive ? strncmp(ptr, search, searchLen) : strncasecmp(ptr, search, searchLen)) == 0)
{
if (replaceLen > searchLen)
{
/* First, see if we have enough space to do this operation */
if (maxLen - textLen < replaceLen - searchLen)
{
/* First, see if the replacement length goes out of bounds. */
if (browsed + replaceLen >= maxLen)
{
/* EXAMPLE CASE:
* Subject: AABBBCCC
* Buffer : 12 bytes
* Search : BBB
* Replace: DDDDDDDDDD
* OUTPUT : AADDDDDDDDD
* POSITION: ^
*/
/* If it does, we'll just bound the length and do a strcpy. */
replaceLen = maxLen - browsed;
/* Note, we add one to the final result for the null terminator */
strncopy(ptr, replace, replaceLen + 1);
/* Don't truncate a multi-byte character */
if (*(ptr + replaceLen - 1) & 1 << 7)
{
replaceLen -= UTIL_CheckValidChar(ptr + replaceLen - 1);
*(ptr + replaceLen) = '\0';
}
}
else
{
/* EXAMPLE CASE:
* Subject: AABBBCCC
* Buffer : 12 bytes
* Search : BBB
* Replace: DDDDDDD
* OUTPUT : AADDDDDDDCC
* POSITION: ^
*/
/* We're going to have some bytes left over... */
size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
char *moveTo = ptr + replaceLen;
/* First, move our old data out of the way. */
memmove(moveTo, moveFrom, realBytesToCopy);
/* Now, do our replacement. */
memcpy(ptr, replace, replaceLen);
}
}
else
{
/* EXAMPLE CASE:
* Subject: AABBBCCC
* Buffer : 12 bytes
* Search : BBB
* Replace: DDDD
* OUTPUT : AADDDDCCC
* POSITION: ^
*/
/* Yes, we have enough space. Do a normal move operation. */
char *moveFrom = ptr + searchLen;
char *moveTo = ptr + replaceLen;
/* First move our old data out of the way. */
size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
memmove(moveTo, moveFrom, bytesToCopy);
/* Now do our replacement. */
memcpy(ptr, replace, replaceLen);
}
}
else if (replaceLen < searchLen)
{
/* EXAMPLE CASE:
* Subject: AABBBCCC
* Buffer : 12 bytes
* Search : BBB
* Replace: D
* OUTPUT : AADCCC
* POSITION: ^
*/
/* If the replacement does not grow the string length, we do not
* need to do any fancy checking at all. Yay!
*/
char *moveFrom = ptr + searchLen; /* Start after the search pointer */
char *moveTo = ptr + replaceLen; /* Copy to where the replacement ends */
/* Copy our replacement in, if any */
if (replaceLen)
{
memcpy(ptr, replace, replaceLen);
}
/* Figure out how many bytes to move down, including null terminator */
size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
/* Move the rest of the string down */
memmove(moveTo, moveFrom, bytesToCopy);
}
else
{
/* EXAMPLE CASE:
* Subject: AABBBCCC
* Buffer : 12 bytes
* Search : BBB
* Replace: DDD
* OUTPUT : AADDDCCC
* POSITION: ^
*/
/* We don't have to move anything around, just do a straight copy */
memcpy(ptr, replace, replaceLen);
}
return ptr + replaceLen;
}
ptr++;
browsed++;
}
return NULL;
}