Improve UTF-8 support in some natives (bug 6475) (#407)
* Compile as static library, update AMBuildScript and link to core * Update VS project files to include the library * Add UTF-8 Rewind library (v1.5.1) to third_party directory * Update ACKNOWLEDGEMENTS.txt * Move AMXX buffer in its own function * Move constants from string.inc to string_const.inc and update project files * Move stocks from string.inc to string_stocks.inc and update project files * Improve UTF-8 support in containi() and update documentation * Improve UTF-8 support in strcmp() and update documentation * Improve UTF-8 support in strfind() and update documentation Worth to be noted that this native with ignorecase set was not working properly. So broken that no one reported the issue. This adds also a safety check for "pos" parameter to not go < 0. * Improve UTF-8 support in strncmp() and update documentation * Improve UTF-8 support in equali() and update documentation * Add an option to some UTF-8 Rewind functions for avoiding invalid data to be replaced By default it replaces any invalid byte or sequence of bytes by 0xFFFD (3 bytes). It can be problematic when the input buffer is not changed (from a plugin) and that some natives need to calculate a position from the converted string. With such replacement, the position is displaced due the final string length being larger. This compiles the library as C++, because I added some silly param with a default default value which is not supported by C. * Improve UTF-8 support in replace_string/ex() and update documentation * Add is_string_category() and update documentation * Update a little testsuite plugin (and fix linux compilation) * Add mb_strotolower/upper() and update documentation * Add mb_ucfirst() and update documentation * Add mb_strtotile() and update documentation * Improve UTF-8 support in get_players() and find_player() with name/case insenstive flags set * Fix KliPPy's complain
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
#endif
|
||||
#define _string_included
|
||||
|
||||
#define charsmax(%1) (sizeof(%1)-1)
|
||||
#include <string_const>
|
||||
|
||||
/**
|
||||
* @global Unless otherwise noted, all string functions which take in a
|
||||
@@ -25,11 +25,6 @@
|
||||
* copy(string, charsmax(string), ...)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Buffer size used by fmt().
|
||||
*/
|
||||
#define MAX_FMT_LENGTH 256
|
||||
|
||||
/**
|
||||
* Calculates the length of a string.
|
||||
*
|
||||
@@ -52,13 +47,15 @@ native contain(const source[], const string[]);
|
||||
/**
|
||||
* Tests whether a string is found inside another string with case ignoring.
|
||||
*
|
||||
* @note This supports multi-byte characters (UTF-8) on comparison.
|
||||
*
|
||||
* @param source String to search in.
|
||||
* @param string Substring to find inside the original string.
|
||||
*
|
||||
* @return -1 on failure (no match found). Any other value
|
||||
* indicates a position in the string where the match starts.
|
||||
*/
|
||||
native containi(const source[],const string[]);
|
||||
native containi(const source[], const string[]);
|
||||
|
||||
/**
|
||||
* Given a string, replaces the first occurrence of a search string with a
|
||||
@@ -83,6 +80,7 @@ native replace(text[], len, const what[], const with[]);
|
||||
* that pushes old data out.
|
||||
*
|
||||
* @note Only available in 1.8.3 and above.
|
||||
* @note This supports multi-byte characters (UTF-8) on case insensitive comparison.
|
||||
*
|
||||
* @param text String to perform search and replacements on.
|
||||
* @param maxlength Maximum length of the string buffer.
|
||||
@@ -92,7 +90,7 @@ native replace(text[], len, const what[], const with[]);
|
||||
*
|
||||
* @return Number of replacements that were performed.
|
||||
*/
|
||||
native replace_string(text[], maxlength, const search[], const replace[], bool:caseSensitive=true);
|
||||
native replace_string(text[], maxlength, const search[], const replace[], bool:caseSensitive = true);
|
||||
|
||||
/**
|
||||
* Given a string, replaces the first occurrence of a search string with a
|
||||
@@ -104,6 +102,7 @@ native replace_string(text[], maxlength, const search[], const replace[], bool:c
|
||||
* that pushes old data out.
|
||||
*
|
||||
* @note Only available in 1.8.3 and above.
|
||||
* @note This supports multi-byte characters (UTF-8) on case insensitive comparison.
|
||||
*
|
||||
* @param text String to perform search and replacements on.
|
||||
* @param maxlength Maximum length of the string buffer.
|
||||
@@ -119,7 +118,7 @@ native replace_string(text[], maxlength, const search[], const replace[], bool:c
|
||||
* the last replacement ended, or -1 if no replacements were
|
||||
* made.
|
||||
*/
|
||||
native replace_stringex(text[], maxlength, const search[], const replace[], searchLen=-1, replaceLen=-1, bool:caseSensitive=true);
|
||||
native replace_stringex(text[], maxlength, const search[], const replace[], searchLen = -1, replaceLen = -1, bool:caseSensitive = true);
|
||||
|
||||
/**
|
||||
* Concatenates one string onto another.
|
||||
@@ -361,13 +360,15 @@ native equal(const a[],const b[],c=0);
|
||||
/**
|
||||
* Returns whether two strings are equal with case ignoring.
|
||||
*
|
||||
* @note This supports multi-byte characters (UTF-8) on comparison.
|
||||
*
|
||||
* @param a First string (left).
|
||||
* @param b Second string (right).
|
||||
* @param c Number of characters to compare.
|
||||
*
|
||||
* @return True if equal, false otherwise.
|
||||
*/
|
||||
native equali(const a[],const b[],c=0);
|
||||
native equali(const a[], const b[], c = 0);
|
||||
|
||||
/**
|
||||
* Copies one string to another string.
|
||||
@@ -445,49 +446,6 @@ native parse(const text[], ... );
|
||||
* @noreturn
|
||||
*/
|
||||
native strtok(const text[], Left[], leftLen, Right[], rightLen, token=' ', trimSpaces=0);
|
||||
|
||||
/**
|
||||
* Below are the trim flags for strtok2
|
||||
*
|
||||
* You can specify how the left and right buffers will
|
||||
* be trimmed by strtok2. LTRIM trims spaces from the
|
||||
* left side. RTRIM trims from the right side.
|
||||
*
|
||||
* The defines TRIM_INNER, TRIM_OUTER and TRIM_FULL are
|
||||
* shorthands for commonly used flag combinations.
|
||||
*
|
||||
* When the initial string is trimmed, using TRIM_INNER
|
||||
* for all subsequent strtok2 calls will ensure that left
|
||||
* and right are always trimmed from both sides.
|
||||
*
|
||||
* Examples:
|
||||
* str1[] = " This is * some text "
|
||||
* strtok2(str1, left, 24, right, 24, '*', TRIM_FULL)
|
||||
* left will be "This is", right will be "some text"
|
||||
*
|
||||
* str2[] = " Here is | an | example "
|
||||
* trim(str2)
|
||||
* strtok2(str2, left, 24, right, 24, '|', TRIM_INNER)
|
||||
* left will be "Here is", right will be "an | example"
|
||||
* strtok2(right, left, 24, right, 24, '|', TRIM_INNER)
|
||||
* left will be "an", right will be "example"
|
||||
*
|
||||
* str3[] = " One - more "
|
||||
* strtok2(str3, left, 24, right, 24, '-', TRIM_OUTER)
|
||||
* left will be "One ", right will be " more"
|
||||
*
|
||||
* str4[] = " Final . example "
|
||||
* strtok2(str4, left, 24, right, 24, '.', LTRIM_LEFT|LTRIM_RIGHT)
|
||||
* left will be "Final ", right will be "example "
|
||||
*/
|
||||
#define LTRIM_LEFT (1<<0)
|
||||
#define RTRIM_LEFT (1<<1)
|
||||
#define LTRIM_RIGHT (1<<2)
|
||||
#define RTRIM_RIGHT (1<<3)
|
||||
|
||||
#define TRIM_INNER RTRIM_LEFT|LTRIM_RIGHT
|
||||
#define TRIM_OUTER LTRIM_LEFT|RTRIM_RIGHT
|
||||
#define TRIM_FULL TRIM_OUTER|TRIM_INNER
|
||||
|
||||
/**
|
||||
* Breaks a string in two by token.
|
||||
@@ -523,6 +481,21 @@ native trim(text[]);
|
||||
*/
|
||||
native strtolower(string[]);
|
||||
|
||||
/**
|
||||
* Performs a multi-byte safe (UTF-8) conversion of all chars in string to lower case.
|
||||
*
|
||||
* @note Although most code points can be converted in-place, there are notable
|
||||
* exceptions and the final length can vary.
|
||||
* @note Case mapping is not reversible. That is, toUpper(toLower(x)) != toLower(toUpper(x)).
|
||||
*
|
||||
* @param string The string to convert.
|
||||
* @param maxlength Optional size of the buffer. If 0, the length of the original string
|
||||
* will be used instead.
|
||||
*
|
||||
* @return Number of bytes written.
|
||||
*/
|
||||
native mb_strtolower(string[], maxlength = 0);
|
||||
|
||||
/**
|
||||
* Converts all chars in string to upper case.
|
||||
*
|
||||
@@ -531,6 +504,21 @@ native strtolower(string[]);
|
||||
*/
|
||||
native strtoupper(string[]);
|
||||
|
||||
/**
|
||||
* Performs a multi-byte safe (UTF-8) conversion of all chars in string to upper case.
|
||||
*
|
||||
* @note Although most code points can be converted in-place, there are notable
|
||||
* exceptions and the final length can vary.
|
||||
* @note Case mapping is not reversible. That is, toUpper(toLower(x)) != toLower(toUpper(x)).
|
||||
*
|
||||
* @param string The string to convert.
|
||||
* @param maxlength Optional size of the buffer. If 0, the length of the original string
|
||||
* will be used instead.
|
||||
*
|
||||
* @return Number of bytes written.
|
||||
*/
|
||||
native mb_strtoupper(string[], maxlength = 0);
|
||||
|
||||
/**
|
||||
* Make a string's first character uppercase.
|
||||
*
|
||||
@@ -539,6 +527,68 @@ native strtoupper(string[]);
|
||||
*/
|
||||
native ucfirst(string[]);
|
||||
|
||||
/**
|
||||
* Performs a multi-byte safe (UTF-8) conversion of a string's first character to upper case.
|
||||
*
|
||||
* @note Although most code points can be converted in-place, there are notable
|
||||
* exceptions and the final length can vary.
|
||||
*
|
||||
* @param string The string to convert.
|
||||
* @param maxlength Optional size of the buffer. If 0, the length of the original string
|
||||
* will be used instead.
|
||||
*
|
||||
* @return Number of bytes written.
|
||||
*/
|
||||
native mb_ucfirst(string[], maxlength = 0);
|
||||
|
||||
/**
|
||||
* Performs a multi-byte safe (UTF-8) conversion of all chars in string to title case.
|
||||
*
|
||||
* @note Although most code points can be converted in-place, there are notable
|
||||
* exceptions and the final length can vary.
|
||||
* @note Any type of punctuation can break up a word, even if this is
|
||||
* not grammatically valid. This happens because the titlecasing algorithm
|
||||
* does not and cannot take grammar rules into account.
|
||||
* @note Examples:
|
||||
* The running man | The Running Man
|
||||
* NATO Alliance | Nato Alliance
|
||||
* You're amazing at building libraries | You'Re Amazing At Building Libraries
|
||||
*
|
||||
* @param string The string to convert.
|
||||
* @param maxlength Optional size of the buffer. If 0, the length of the original string
|
||||
* will be used instead.
|
||||
*
|
||||
* @return Number of bytes written.
|
||||
*/
|
||||
native mb_strtotitle(string[], maxlength = 0);
|
||||
|
||||
/**
|
||||
* Checks if the input string conforms to the category specified by the flags.
|
||||
*
|
||||
* @note This function can be used to check if the code points in a string are part
|
||||
* of a category. Valid flags are part of the UTF8C_* list of defines.
|
||||
* The category for a code point is defined as part of the entry in
|
||||
* UnicodeData.txt, the data file for the Unicode code point database.
|
||||
* @note Flags parameter must be a combination of UTF8C_* flags or a single UTF8C_IS* flag.
|
||||
* In order to main backwards compatibility with POSIX functions like `isdigit`
|
||||
* and `isspace`, compatibility flags have been provided. Note, however, that
|
||||
* the result is only guaranteed to be correct for code points in the Basic
|
||||
* Latin range, between U+0000 and 0+007F. Combining a compatibility flag with
|
||||
* a regular category flag will result in undefined behavior.
|
||||
* @note The function is greedy. This means it will try to match as many code
|
||||
* points with the matching category flags as possible and return the offset in
|
||||
* the input in bytes.
|
||||
*
|
||||
* @param input The string to check
|
||||
* @param input_size Size of the string, use 1 to check one character regardless its size
|
||||
* @param flags Requested category, see UTF8C_* flags
|
||||
* @param output_size Number of bytes in the input that conform to the specified
|
||||
* category flags
|
||||
* @return True if the whole input of `input_size` conforms to the specified
|
||||
* category flags, false otherwise
|
||||
*/
|
||||
native bool:is_string_category(const input[], input_size, flags, &output_size = 0);
|
||||
|
||||
/**
|
||||
* Returns whether a character is numeric.
|
||||
*
|
||||
@@ -612,23 +662,6 @@ native bool:is_char_upper(ch);
|
||||
*/
|
||||
native bool:is_char_lower(ch);
|
||||
|
||||
/**
|
||||
* Returns whether a given string contains only digits.
|
||||
* This returns false for zero-length strings.
|
||||
*
|
||||
* @param sString Character to test.
|
||||
* @return True if string contains only digit, otherwise false.
|
||||
*/
|
||||
stock bool:is_str_num(const sString[])
|
||||
{
|
||||
new i = 0;
|
||||
|
||||
while (sString[i] && isdigit(sString[i]))
|
||||
++i;
|
||||
|
||||
return sString[i] == 0 && i != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes a character is using. This is
|
||||
* for multi-byte characters (UTF-8). For normal ASCII characters,
|
||||
@@ -641,42 +674,6 @@ stock bool:is_str_num(const sString[])
|
||||
*/
|
||||
native get_char_bytes(const source[]);
|
||||
|
||||
/**
|
||||
* Returns an uppercase character to a lowercase character.
|
||||
*
|
||||
* @note Only available in 1.8.3 and above.
|
||||
*
|
||||
* @param chr Characer to convert.
|
||||
* @return Lowercase character on success,
|
||||
* no change on failure.
|
||||
*/
|
||||
stock char_to_upper(chr)
|
||||
{
|
||||
if (is_char_lower(chr))
|
||||
{
|
||||
return (chr & ~(1<<5));
|
||||
}
|
||||
return chr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a lowercase character to an uppercase character.
|
||||
*
|
||||
* @note Only available in 1.8.3 and above.
|
||||
*
|
||||
* @param chr Characer to convert.
|
||||
* @return Uppercase character on success,
|
||||
* no change on failure.
|
||||
*/
|
||||
stock char_to_lower(chr)
|
||||
{
|
||||
if (is_char_upper(chr))
|
||||
{
|
||||
return (chr | (1<<5));
|
||||
}
|
||||
return chr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates one string onto another.
|
||||
*
|
||||
@@ -690,19 +687,23 @@ native strcat(dest[], const source[], maxlength);
|
||||
/**
|
||||
* Tests whether a string is found inside another string.
|
||||
*
|
||||
* @note This supports multi-byte characters (UTF-8) on case insensitive comparison.
|
||||
*
|
||||
* @param string String to search in.
|
||||
* @param sub Substring to find inside the original string.
|
||||
* @param ignorecase If true, search is case insensitive.
|
||||
* If false (default), search is case sensitive.
|
||||
* @param pos
|
||||
* @param pos Start position to search from.
|
||||
* @return -1 on failure (no match found). Any other value
|
||||
* indicates a position in the string where the match starts.
|
||||
*/
|
||||
native strfind(const string[], const sub[], ignorecase=0, pos=0);
|
||||
native strfind(const string[], const sub[], bool:ignorecase = false, pos = 0);
|
||||
|
||||
/**
|
||||
* Compares two strings lexographically.
|
||||
*
|
||||
* @note This supports multi-byte characters (UTF-8) on case insensitive comparison.
|
||||
*
|
||||
* @param string1 First string (left).
|
||||
* @param string2 Second string (right).
|
||||
* @param ignorecase If true, comparison is case insensitive.
|
||||
@@ -711,12 +712,13 @@ native strfind(const string[], const sub[], ignorecase=0, pos=0);
|
||||
* 0 if string1 == string2
|
||||
* 1 if string1 > string2
|
||||
*/
|
||||
native strcmp(const string1[], const string2[], ignorecase=0);
|
||||
native strcmp(const string1[], const string2[], bool:ignorecase = false);
|
||||
|
||||
/**
|
||||
* Compares two strings parts lexographically.
|
||||
*
|
||||
* @note Only available in 1.8.3 and above.
|
||||
* @note This supports multi-byte characters (UTF-8) on case insensitive comparison.
|
||||
*
|
||||
* @param string1 First string (left).
|
||||
* @param string2 Second string (right).
|
||||
@@ -727,17 +729,7 @@ native strcmp(const string1[], const string2[], ignorecase=0);
|
||||
* 0 if string1 == string2
|
||||
* 1 if string1 > string2
|
||||
*/
|
||||
native strncmp(const string1[], const string2[], num, bool:ignorecase=false);
|
||||
|
||||
/**
|
||||
* Backwards compatibility stock - use argbreak or argparse.
|
||||
* @deprecated this function does not work properly.
|
||||
*/
|
||||
#pragma deprecated Use argbreak() instead
|
||||
stock strbreak(const text[], Left[], leftLen, Right[], rightLen)
|
||||
{
|
||||
return argbreak(text, Left, leftLen, Right, rightLen);
|
||||
}
|
||||
native strncmp(const string1[], const string2[], num, bool:ignorecase = false);
|
||||
|
||||
/**
|
||||
* Parses an argument string to find the first argument. You can use this to
|
||||
@@ -773,34 +765,6 @@ stock strbreak(const text[], Left[], leftLen, Right[], rightLen)
|
||||
*/
|
||||
native argparse(const text[], pos, argbuffer[], maxlen);
|
||||
|
||||
/**
|
||||
* Emulates strbreak() using argparse().
|
||||
*
|
||||
* @param text Source input string.
|
||||
* @param left Buffer to store string left part.
|
||||
* @param leftlen Maximum length of the string part buffer.
|
||||
* @param right Buffer to store string right part.
|
||||
* @param rightlen Maximum length of the string part buffer.
|
||||
*
|
||||
* @return -1 if no match was found; otherwise, an index into source
|
||||
* marking the first index after the searched text. The
|
||||
* index is always relative to the start of the input string.
|
||||
*/
|
||||
stock argbreak(const text[], left[], leftlen, right[], rightlen)
|
||||
{
|
||||
new pos = argparse(text, 0, left, leftlen);
|
||||
|
||||
if (pos == -1)
|
||||
return -1;
|
||||
|
||||
new textlen = strlen(text);
|
||||
while (pos < textlen && isspace(text[pos]))
|
||||
pos++;
|
||||
|
||||
copy(right, rightlen, text[pos]);
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text in a string up until a certain character sequence is reached.
|
||||
*
|
||||
@@ -817,195 +781,6 @@ stock argbreak(const text[], left[], leftlen, right[], rightlen)
|
||||
*/
|
||||
native split_string(const source[], const split[], part[], partLen);
|
||||
|
||||
/**
|
||||
* It is basically strbreak but you have a delimiter that is more than one character in length. By Suicid3.
|
||||
*
|
||||
* @param szInput Source input string.
|
||||
* @param szLeft Buffer to store left string part.
|
||||
* @param pL_Max Maximum length of the string part buffer.
|
||||
* @param szRight Buffer to store right string part.
|
||||
* @param pR_Max Maximum length of the string part buffer.
|
||||
* @param szDelim A string which specifies a search point to break at.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
stock split(const szInput[], szLeft[], pL_Max, szRight[], pR_Max, const szDelim[])
|
||||
{
|
||||
new iEnd = contain(szInput, szDelim);
|
||||
new iStart = iEnd + strlen(szDelim);
|
||||
|
||||
//If delimiter isnt in Input just split the string at max lengths
|
||||
if (iEnd == -1)
|
||||
{
|
||||
iStart = copy(szLeft, pL_Max, szInput);
|
||||
copy(szRight, pR_Max, szInput[iStart]);
|
||||
return;
|
||||
}
|
||||
|
||||
//If delimter is in Input then split at input for max lengths
|
||||
if (pL_Max >= iEnd)
|
||||
copy(szLeft, iEnd, szInput);
|
||||
else
|
||||
copy(szLeft, pL_Max, szInput);
|
||||
|
||||
copy(szRight, pR_Max, szInput[iStart]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a path from szFilePath leaving the name of the file in szFile for a pMax length.
|
||||
*
|
||||
* @param szFilePath String to perform search and replacements on.
|
||||
* @param szFile Buffer to store file name.
|
||||
* @param pMax Maximum length of the string buffer.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
stock remove_filepath(const szFilePath[], szFile[], pMax)
|
||||
{
|
||||
new len = strlen(szFilePath);
|
||||
|
||||
while ((--len >= 0) && (szFilePath[len] != '/') && (szFilePath[len] != '\')) { }
|
||||
|
||||
copy(szFile, pMax, szFilePath[len + 1]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a contained string iteratively.
|
||||
*
|
||||
* @note Consider using replace_string() instead.
|
||||
*
|
||||
* @note This ensures that no infinite replacements will take place by
|
||||
* intelligently moving to the next string position each iteration.
|
||||
*
|
||||
* @param string String to perform search and replacements on.
|
||||
* @param len Maximum length of the string buffer.
|
||||
* @param what String to search for.
|
||||
* @param with String to replace the search string with.
|
||||
|
||||
* @return Number of replacements on success, otherwise 0.
|
||||
*/
|
||||
stock replace_all(string[], len, const what[], const with[])
|
||||
{
|
||||
new pos = 0;
|
||||
|
||||
if ((pos = contain(string, what)) == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
new total = 0;
|
||||
new with_len = strlen(with);
|
||||
new diff = strlen(what) - with_len;
|
||||
new total_len = strlen(string);
|
||||
new temp_pos = 0;
|
||||
|
||||
while (replace(string[pos], len - pos, what, with) != 0)
|
||||
{
|
||||
total++;
|
||||
|
||||
/* jump to position after replacement */
|
||||
pos += with_len;
|
||||
|
||||
/* update cached length of string */
|
||||
total_len -= diff;
|
||||
|
||||
/* will the next call be operating on the last character? */
|
||||
if (pos >= total_len)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* find the next position from our offset */
|
||||
temp_pos = contain(string[pos], what);
|
||||
|
||||
/* if it's invalid, we're done */
|
||||
if (temp_pos == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* otherwise, reposition and update counters */
|
||||
pos += temp_pos;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks a string into pieces and stores each piece into an array of buffers.
|
||||
*
|
||||
* @param text The string to split.
|
||||
* @param split The string to use as a split delimiter.
|
||||
* @param buffers An array of string buffers (2D array).
|
||||
* @param maxStrings Number of string buffers (first dimension size).
|
||||
* @param maxStringLength Maximum length of each string buffer.
|
||||
* @param copyRemainder False (default) discard excess pieces, true to ignore
|
||||
* delimiters after last piece.
|
||||
* @return Number of strings retrieved.
|
||||
*/
|
||||
stock explode_string(const text[], const split[], buffers[][], maxStrings, maxStringLength, bool:copyRemainder = false)
|
||||
{
|
||||
new reloc_idx, idx, total;
|
||||
|
||||
if (maxStrings < 1 || !split[0])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((idx = split_string(text[reloc_idx], split, buffers[total], maxStringLength)) != -1)
|
||||
{
|
||||
reloc_idx += idx;
|
||||
if (++total == maxStrings)
|
||||
{
|
||||
if (copyRemainder)
|
||||
{
|
||||
copy(buffers[total-1], maxStringLength, text[reloc_idx-idx]);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
copy(buffers[total++], maxStringLength, text[reloc_idx]);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins an array of strings into one string, with a "join" string inserted in
|
||||
* between each given string. This function complements ExplodeString.
|
||||
*
|
||||
* @param strings An array of strings.
|
||||
* @param numStrings Number of strings in the array.
|
||||
* @param join The join string to insert between each string.
|
||||
* @param buffer Output buffer to write the joined string to.
|
||||
* @param maxLength Maximum length of the output buffer.
|
||||
* @return Number of bytes written to the output buffer.
|
||||
*/
|
||||
stock implode_strings(const strings[][], numStrings, const join[], buffer[], maxLength)
|
||||
{
|
||||
new total, length, part_length;
|
||||
new join_length = strlen(join);
|
||||
for (new i=0; i<numStrings; i++)
|
||||
{
|
||||
length = copy(buffer[total], maxLength-total, strings[i]);
|
||||
total += length;
|
||||
if (length < part_length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (i != numStrings - 1)
|
||||
{
|
||||
length = copy(buffer[total], maxLength-total, join);
|
||||
total += length;
|
||||
if (length < join_length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
// Always keep this at the bottom of this file.
|
||||
#include <string_stocks>
|
||||
|
Reference in New Issue
Block a user