Merge pull request #2 from alliedmodders/strbreak

Add new variants of strbreak, which is too broken to use (bug 6041, r=ds).
This commit is contained in:
David Anderson 2014-02-10 00:06:06 -08:00
commit 3772645fca
4 changed files with 458 additions and 24 deletions

View File

@ -926,12 +926,55 @@ static cell AMX_NATIVE_CALL amx_strtok2(AMX *amx, cell *params)
return pos; return pos;
} }
//added by BAILOPAN // native argparse(const text[], pos, buffer, maxlen);
static cell AMX_NATIVE_CALL argparse(AMX *amx, cell *params)
{
int temp;
const char *input = get_amxstring(amx, params[1], 0, temp);
size_t input_len = size_t(temp);
size_t start_pos = size_t(params[2]);
cell *buffer = get_amxaddr(amx, params[3]);
size_t buflen = size_t(params[4]);
// Strip all left-hand whitespace.
size_t i = start_pos;
while (i < input_len && isspace(input[i]))
i++;
if (i >= input_len) {
*buffer = '\0';
return -1;
}
cell *bufpos = buffer;
bool in_quote = false;
for (; i < input_len; i++) {
// Ignore quotes, except as an indicator as to whether to stop
// at a space.
if (input[i] == '"') {
in_quote = !in_quote;
continue;
}
// If not in quotes, and we see a space, stop.
if (isspace(input[i]) && !in_quote)
break;
if (size_t(bufpos - buffer) < buflen)
*bufpos++ = input[i];
}
*bufpos = '\0';
return i;
}
//added by BAILOPAN :(
//Takes a string and breaks it into a 1st param and rest params //Takes a string and breaks it into a 1st param and rest params
//strbreak(String[], First[], FirstLen, Rest[], RestLen) //strbreak(String[], First[], FirstLen, Rest[], RestLen)
static cell AMX_NATIVE_CALL strbreak(AMX *amx, cell *params) /* 5 param */ static cell AMX_NATIVE_CALL strbreak(AMX *amx, cell *params) /* 5 param */
{ {
int _len; int _len;
bool in_quote = false; bool in_quote = false;
bool had_quotes = false; bool had_quotes = false;
@ -946,9 +989,9 @@ static cell AMX_NATIVE_CALL strbreak(AMX *amx, cell *params) /* 5 param */
size_t len = (size_t)_len; size_t len = (size_t)_len;
while (isspace(string[i]) && i<len) while (isspace(string[i]) && i<len)
i++; i++;
beg = i; beg = i;
for (; i<len; i++) for (; i<len; i++)
{ {
if (string[i] == '"' && !in_quote) if (string[i] == '"' && !in_quote)
@ -968,15 +1011,18 @@ do_copy:
const char *start = had_quotes ? &(string[beg+1]) : &(string[beg]); const char *start = had_quotes ? &(string[beg+1]) : &(string[beg]);
size_t _end = had_quotes ? (i==len-1 ? 1 : 2) : 0; size_t _end = had_quotes ? (i==len-1 ? 1 : 2) : 0;
size_t end = (pos - _end > (size_t)LeftMax) ? (size_t)LeftMax : pos - _end; size_t end = (pos - _end > (size_t)LeftMax) ? (size_t)LeftMax : pos - _end;
size_t to_go = end-beg;
if (end && to_go) // If there is anything to copy, make sure we copy min(maxlen, slicelen).
{ size_t copylen = end >= beg
while (to_go--) ? ((end - beg > size_t(LeftMax))
*left++ = (cell)*start++; ? size_t(LeftMax)
} : end - beg
*left = '\0'; )
: 0;
set_amxstring(amx, params[2], start, copylen);
end = (len-i+1 > (size_t)RightMax) ? (size_t)RightMax : len-i+1; end = (len-i+1 > (size_t)RightMax) ? (size_t)RightMax : len-i+1;
if (end) if (end)
{ {
start = &(string[i]); start = &(string[i]);
while (end--) while (end--)
@ -1224,6 +1270,7 @@ AMX_NATIVE_INFO string_Natives[] =
{"replace", replace}, {"replace", replace},
{"setc", setc}, {"setc", setc},
{"strbreak", strbreak}, {"strbreak", strbreak},
{"argparse", argparse},
{"strtolower", strtolower}, {"strtolower", strtolower},
{"strtoupper", strtoupper}, {"strtoupper", strtoupper},
{"str_to_num", strtonum}, {"str_to_num", strtonum},

View File

@ -223,16 +223,6 @@ native strtok(const text[], Left[], leftLen, Right[], rightLen, token=' ', trimS
*/ */
native strtok2(const text[], left[], const llen, right[], const rlen, const token = ' ', const trim = 0); native strtok2(const text[], left[], const llen, right[], const rlen, const token = ' ', const trim = 0);
/* Gets parameters from text one at a time
It breaks a string into the first parameter and the rest of the parameters
(A left side and right side of the string)
Example: to split text: "^"This is^" the best year",
strbreak(text, arg1, len1, arg2, len2)
arg1="This is", arg2=the best year
This is more useful than parse() because you can keep breaking
any number of arguments */
native strbreak(const text[], Left[], leftLen, Right[], rightLen);
/* Strips spaces from the beginning and end of a string. */ /* Strips spaces from the beginning and end of a string. */
native trim(text[]); native trim(text[]);
@ -277,6 +267,58 @@ stock bool:is_str_num(const sString[])
return sString[i] == 0 && i != 0; return sString[i] == 0 && i != 0;
} }
// Warning: this function is deprecated as it does not work properly. Use
// argparse() or argbreak().
native strbreak(const text[], Left[], leftLen, Right[], rightLen);
/**
* Parses an argument string to find the first argument. You can use this to
* replace strbreak().
*
* You can use argparse() to break a string into all of its arguments:
* new arg[N], pos;
* while (true) {
* pos = argparse(string, pos, arg, sizeof(arg) - 1);
* if (pos == -1)
* break;
* }
*
* All initial whitespace is removed. Remaining characters are read until an
* argument separator is encountered. A separator is any whitespace not inside
* a double-quotation pair (i.e. "x b" is one argument). If only one quotation
* mark appears, argparse() acts as if one existed at the end of the string.
* Quotation marks are never written back, and do not act as separators. For
* example, "a""b""c" will return "abc". An empty quote pair ("") will count
* as an argument containing no characters.
*
* argparse() will write an empty string to argbuffer if no argument is found.
*
* @param text String to tokenize.
* @param pos Position to start parsing from.
* @param argbuffer Buffer to store first argument.
* @param maxlen Size of the buffer.
* @return If no argument was found, -1 is returned. Otherwise,
* the index to the next position to parse from is
* returned. This might be the very end of the string.
*/
native argparse(const text[], pos, argbuffer[], maxlen);
/* Emulates strbreak() using argparse(). */
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;
}
/* It is basically strbreak but you have a delimiter that is more than one character in length. /* It is basically strbreak but you have a delimiter that is more than one character in length.
You pass the Input string, the Left output, the max length of the left output, You pass the Input string, the Left output, the max length of the left output,
the right output , the max right length, and then the delimiter string. the right output , the max right length, and then the delimiter string.

View File

@ -0,0 +1,339 @@
// vim: set ts=4 sw=4 tw=99 sts=4 noet ft=c:
#include <amxmodx>
#include <amxmisc>
#pragma ctrlchar '\'
public plugin_init()
{
register_plugin("strbreak tests", "1.0", "BAILOPAN");
register_concmd("test_strbreak", "test_strbreak", 0, "admin");
register_concmd("test_argparse", "test_argparse", 0, "admin");
}
new TestCount = 0;
new FailCount = 0;
test_reset()
{
TestCount = 0;
FailCount = 0;
}
test_numequal(a, b)
{
TestCount++;
if (a == b) {
server_print("[%d] PASS /%d/ == /%d/", TestCount, a, b);
} else {
server_print("[%d] FAIL /%d/ == /%d/", TestCount, a, b);
FailCount++;
}
}
test_equal(a[], b[])
{
TestCount++;
if (equal(a, b)) {
server_print("[%d] PASS /%s/ == /%s/", TestCount, a, b);
} else {
server_print("[%d] FAIL /%s/ == /%s/", TestCount, a, b);
FailCount++;
}
}
public test_strbreak(id)
{
test_reset();
new left[8], right[8];
// Test sort of normal behavior for strbreak().
strbreak("a b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "b c");
strbreak("a", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "");
strbreak("a\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a\"");
test_equal(right, "");
strbreak("\"a\" yy", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "yy");
strbreak("\"a x", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "\"a x");
test_equal(right, "");
strbreak("a b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "b c");
strbreak("\"a\" b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "b c");
strbreak("a \"b c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "\"b c\"");
strbreak("a q\"b c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "q\"b c\"");
strbreak("q \"b c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "q");
test_equal(right, "\"b c\"");
strbreak("q \"b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "q");
test_equal(right, "\"b c");
// strbreak() functionality starts degrading here, but we test this to
// preserve bug-for-bug compatibility.
strbreak("q\"b c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "\"b c");
test_equal(right, "\"");
strbreak("\"a \"a \"b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a \"");
test_equal(right, "\"b c");
strbreak("\"a \"a b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a \"");
test_equal(right, "b c");
strbreak("\"a\"a b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a\"");
test_equal(right, "b c");
strbreak("\"a\" x", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a\"");
test_equal(right, "x");
strbreak("\"a\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "\"");
strbreak("\"a\"b", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "\"a\"b");
test_equal(right, "");
strbreak("q\" b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "q\" b c");
test_equal(right, "");
// Test truncation.
strbreak("123456789A 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234567");
strbreak("12345 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "12345");
test_equal(right, "1234567");
strbreak("\"12345\" 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "12345");
test_equal(right, "1234567");
strbreak("\"123456789A\" 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234567");
strbreak("\"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234");
strbreak("\"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234");
strbreak("123456789A 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234");
strbreak("123456789A 123456778923", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234567");
// Bonkers - left-hand makes no sense. Does whitespace count toward the buffer?!
strbreak(" 123456789A 123456778923", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "12345");
test_equal(right, "1234567");
strbreak(" \"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "");
test_equal(right, "1234");
strbreak(" \"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "12");
test_equal(right, "1234");
new text_cmd[7], cheater[64];
strbreak(" a ", text_cmd, 1, cheater, 31);
test_equal(text_cmd, "");
test_equal(cheater, "");
server_print("%d/%d passed", TestCount - FailCount, TestCount);
}
public test_argparse(id)
{
test_reset();
new left[8], right[8];
// Tests for behavior that we expect to work.
argbreak("a b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "b c");
argbreak("a", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "");
argbreak("a\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "");
argbreak("\"a\" yy", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "yy");
argbreak("\"a x", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a x");
test_equal(right, "");
argbreak("a b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "b c");
argbreak("\"a\" b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "b c");
argbreak("a \"b c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "\"b c\"");
argbreak("a q\"b c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "q\"b c\"");
argbreak("q \"b c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "q");
test_equal(right, "\"b c\"");
argbreak("q \"b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "q");
test_equal(right, "\"b c");
argbreak("q\"b c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "qb c");
test_equal(right, "");
argbreak("\"a \"a \"b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a a");
test_equal(right, "\"b c");
argbreak("\"a \"a b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a a");
test_equal(right, "b c");
argbreak("\"a\"a b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "aa");
test_equal(right, "b c");
argbreak("\"a\" x", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "x");
argbreak("\"a\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "a");
test_equal(right, "");
argbreak("\"a\"b", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "ab");
test_equal(right, "");
argbreak("q\" b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "q b c");
test_equal(right, "");
// Test truncation.
argbreak("123456789A 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234567");
argbreak("12345 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "12345");
test_equal(right, "1234567");
argbreak("\"12345\" 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "12345");
test_equal(right, "1234567");
argbreak("\"123456789A\" 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234567");
argbreak("\"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234");
argbreak("\"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234");
argbreak("123456789A 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234");
argbreak("123456789A 123456778923", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234567");
argbreak(" 123456789A 123456778923", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234567");
argbreak(" \"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234");
argbreak(" \"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
test_equal(left, "1234567");
test_equal(right, "1234");
new text_cmd[7], cheater[64];
argbreak(" a ", text_cmd, 1, cheater, 31);
test_equal(text_cmd, "a");
test_equal(cheater, "");
// Test no-finds.
new pos = argparse(" \t\n \t", 0, left, sizeof(left));
test_numequal(pos, -1);
test_equal(left, "");
// Loop for good measure.
new argv[4][10], argc = 0;
new text[] = "a \"b\" cd\"e\" \t f\" ";
pos = 0;
while (argc < 4) {
pos = argparse(text, pos, argv[argc++], 10 - 1);
if (pos == -1)
break;
}
test_numequal(argc, 4);
test_equal(argv[0], "a");
test_equal(argv[1], "b");
test_equal(argv[2], "cde");
test_equal(argv[3], "f ");
server_print("%d/%d passed", TestCount - FailCount, TestCount);
}

View File

@ -66,6 +66,11 @@ def split_all(path):
parts.insert(0, tail) parts.insert(0, tail)
return parts return parts
def copy_binary(source, dest):
builder.AddCopy(source.binary, dest)
if builder.options.debug == '1':
builder.AddCopy(source.debug, dest)
# Create the distribution folder hierarchy. # Create the distribution folder hierarchy.
folder_map = {} folder_map = {}
for folder in folder_list: for folder in folder_list:
@ -74,7 +79,7 @@ for folder in folder_list:
# Copy core dlls. # Copy core dlls.
for dll in AMXX.binaries: for dll in AMXX.binaries:
builder.AddCopy(dll.binary, folder_map['base/addons/amxmodx/dlls']) copy_binary(dll, folder_map['base/addons/amxmodx/dlls'])
# Copy modules. # Copy modules.
for module in AMXX.modules: for module in AMXX.modules:
@ -83,7 +88,7 @@ for module in AMXX.modules:
package = ModPackages[parts[1]] package = ModPackages[parts[1]]
else: else:
package = 'base' package = 'base'
builder.AddCopy(module.binary, folder_map[package + '/addons/amxmodx/modules']) copy_binary(module, folder_map[package + '/addons/amxmodx/modules'])
# Copy the compiler. # Copy the compiler.
builder.AddCopy(AMXX.amxxpc.binary, folder_map['base/addons/amxmodx/scripting']) builder.AddCopy(AMXX.amxxpc.binary, folder_map['base/addons/amxmodx/scripting'])
@ -228,6 +233,7 @@ scripting_files = [
'testsuite/native_test.sma', 'testsuite/native_test.sma',
'testsuite/nvault_test.sma', 'testsuite/nvault_test.sma',
'testsuite/sorttest.sma', 'testsuite/sorttest.sma',
'testsuite/strbreak.sma',
'testsuite/sqlxtest.sma', 'testsuite/sqlxtest.sma',
'testsuite/sqlxtest.sq3', 'testsuite/sqlxtest.sq3',
'testsuite/sqlxtest.sql', 'testsuite/sqlxtest.sql',