int set_amxstring_utf8(AMX *amx, cell amx_addr, const T *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; 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; }
// native regex_substr(Regex:id, str_id, buffer[], maxLen); static cell AMX_NATIVE_CALL regex_substr(AMX *amx, cell *params) { int id = params[1]-1; if (id >= (int)PEL.length() || id < 0 || PEL[id]->isFree()) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid regex handle %d", id); return 0; } RegEx *x = PEL[id]; static char buffer[16384]; // Same as AMXX buffer. size_t length; size_t maxLength = ke::Min<size_t>(params[4], sizeof(buffer) - 1); const char *ret = x->GetSubstring(params[2], buffer, maxLength, &length); if (ret == NULL) { return 0; } if (length >= maxLength && ret[length - 1] & 1 << 7) { maxLength -= UTIL_CheckValidChar((char *)ret + length - 1); } MF_SetAmxString(amx, params[3], ret, maxLength); return 1; }
void AddString(U **buf_p, size_t &maxlen, const S *string, int width, int prec) { int size = 0; U *buf; static S nlstr[] = {'(','n','u','l','l',')','\0'}; buf = *buf_p; if (string == NULL) { string = nlstr; prec = -1; } if (prec >= 0) { for (size = 0; size < prec; size++) { if (string[size] == '\0') break; } } else { while (string[size++]) ; size--; } if (size > (int)maxlen) size = maxlen; /* 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; while (size--) *buf++ = static_cast<U>(*string++); while (width-- > 0 && maxlen) { *buf++ = ' '; maxlen--; } *buf_p = buf; }
size_t atcprintf(D *buffer, size_t maxlen, const S *format, AMX *amx, cell *params, int *param) { int arg; int args = params[0] / sizeof(cell); D *buf_p; D ch; int flags; int width; int prec; int n; //char sign; const S *fmt; size_t llen = maxlen; buf_p = buffer; arg = *param; fmt = format; while (true) { // run through the format string until we hit a '%' or '\0' for (ch = static_cast<D>(*fmt); llen && ((ch = static_cast<D>(*fmt)) != '\0' && ch != '%'); fmt++) { *buf_p++ = static_cast<D>(ch); llen--; } if (ch == '\0' || llen <= 0) goto done; // skip over the '%' fmt++; // reset formatting state flags = 0; width = 0; prec = -1; //sign = '\0'; rflag: ch = static_cast<D>(*fmt++); reswitch: switch(ch) { case '-': flags |= LADJUST; goto rflag; case '.': n = 0; while( is_digit( ( ch = static_cast<D>(*fmt++)) ) ) n = 10 * n + ( ch - '0' ); prec = n < 0 ? -1 : n; goto reswitch; case '0': flags |= ZEROPAD; goto rflag; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; do { n = 10 * n + ( ch - '0' ); ch = static_cast<D>(*fmt++); } while( is_digit( ch ) ); width = n; goto reswitch; case 'c': CHECK_ARGS(0); *buf_p++ = static_cast<D>(*get_amxaddr(amx, params[arg])); 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); AddInt(&buf_p, llen, *get_amxaddr(amx, params[arg]), width, flags); arg++; break; case 'u': CHECK_ARGS(0); AddUInt(&buf_p, llen, static_cast<unsigned int>(*get_amxaddr(amx, params[arg])), width, flags); arg++; break; case 'f': CHECK_ARGS(0); AddFloat(&buf_p, llen, amx_ctof(*get_amxaddr(amx, params[arg])), width, prec, flags); arg++; break; case 'X': CHECK_ARGS(0); flags |= UPPERDIGITS; AddHex(&buf_p, llen, static_cast<unsigned int>(*get_amxaddr(amx, params[arg])), width, flags); arg++; break; case 'x': CHECK_ARGS(0); AddHex(&buf_p, llen, static_cast<unsigned int>(*get_amxaddr(amx, params[arg])), width, flags); arg++; break; case 'a': { CHECK_ARGS(0); // %a is passed a pointer directly to a cell string. cell* ptr=reinterpret_cast<cell*>(*get_amxaddr(amx, params[arg])); if (!ptr) { LogError(amx, AMX_ERR_NATIVE, "Invalid vector string handle provided (%d)", *get_amxaddr(amx, params[arg])); return 0; } AddString(&buf_p, llen, ptr, width, prec); arg++; break; } case 's': CHECK_ARGS(0); AddString(&buf_p, llen, get_amxaddr(amx, params[arg]), width, prec); arg++; break; case 'L': case 'l': { const char *lang; int len; if (ch == 'L') { CHECK_ARGS(1); auto currParam = params[arg++]; lang = playerlang(*get_amxaddr(amx, currParam)); if (!lang) lang = get_amxstring(amx, currParam, 2, len); } else { CHECK_ARGS(0); lang = playerlang(g_langMngr.GetDefLang()); } const char *key = get_amxstring(amx, params[arg++], 3, len); const char *def = translate(amx, lang, key); if (!def) { static char buf[255]; ke::SafeSprintf(buf, sizeof(buf), "ML_NOTFOUND: %s", key); def = buf; } size_t written = atcprintf(buf_p, llen, def, amx, params, &arg); buf_p += written; llen -= written; break; } case 'N': { CHECK_ARGS(0); cell *addr = get_amxaddr(amx, params[arg]); char buffer[255]; if (*addr) { CPlayer *player = NULL; if (*addr >= 1 && *addr <= gpGlobals->maxClients) { player = GET_PLAYER_POINTER_I(*addr); } if (!player || !player->initialized) { LogError(amx, AMX_ERR_NATIVE, "Client index %d is invalid", *addr); return 0; } const char *auth = GETPLAYERAUTHID(player->pEdict); if (!auth || auth[0] == '\0') { auth = "STEAM_ID_PENDING"; } int userid = GETPLAYERUSERID(player->pEdict); ke::SafeSprintf(buffer, sizeof(buffer), "%s<%d><%s><%s>", player->name.chars(), userid, auth, player->team.chars()); } else { ke::SafeSprintf(buffer, sizeof(buffer), "Console<0><Console><Console>"); } AddString(&buf_p, llen, buffer, width, prec); arg++; break; } case 'n': { CHECK_ARGS(0); cell *addr = get_amxaddr(amx, params[arg]); const char *name = "Console"; if (*addr) { CPlayer *player = NULL; if (*addr >= 1 && *addr <= gpGlobals->maxClients) { player = GET_PLAYER_POINTER_I(*addr); } if (!player || !player->initialized) { LogError(amx, AMX_ERR_NATIVE, "Client index %d is invalid", *addr); return 0; } name = player->name.chars(); } AddString(&buf_p, llen, name, width, prec); arg++; break; } case '%': *buf_p++ = static_cast<D>(ch); if (!llen) goto done; llen--; break; case '\0': *buf_p++ = static_cast<D>('%'); if (!llen) goto done; llen--; goto done; break; default: *buf_p++ = static_cast<D>(ch); if (!llen) goto done; llen--; break; } } 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; }
/** * 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; }