Add argparse and argbreak functions (bug 6041, r=ds).
authorDavid Anderson <dvander@alliedmods.net>
Mon Feb 10 00:10:27 2014 -0800 (2014-02-10)
changeset 192cc7e9dd2a341
parent 191 94f5b929bb03
child 193 1d0d1b1e612b
Add argparse and argbreak functions (bug 6041, r=ds).
amxmodx/string.cpp
plugins/include/string.inc
plugins/testsuite/strbreak.sma
     1.1 --- a/amxmodx/string.cpp	Wed Jan 22 20:03:52 2014 -0600
     1.2 +++ b/amxmodx/string.cpp	Mon Feb 10 00:10:27 2014 -0800
     1.3 @@ -926,12 +926,55 @@
     1.4  	return pos;
     1.5  }
     1.6  
     1.7 -//added by BAILOPAN
     1.8 +// native argparse(const text[], pos, buffer, maxlen);
     1.9 +static cell AMX_NATIVE_CALL argparse(AMX *amx, cell *params)
    1.10 +{
    1.11 +	int temp;
    1.12 +	const char *input = get_amxstring(amx, params[1], 0, temp);
    1.13 +	size_t input_len = size_t(temp);
    1.14 +	size_t start_pos = size_t(params[2]);
    1.15 +
    1.16 +	cell *buffer = get_amxaddr(amx, params[3]);
    1.17 +	size_t buflen = size_t(params[4]);
    1.18 +
    1.19 +	// Strip all left-hand whitespace.
    1.20 +	size_t i = start_pos;
    1.21 +	while (i < input_len && isspace(input[i]))
    1.22 +		i++;
    1.23 +
    1.24 +	if (i >= input_len) {
    1.25 +		*buffer = '\0';
    1.26 +		return -1;
    1.27 +	}
    1.28 +
    1.29 +	cell *bufpos = buffer;
    1.30 +
    1.31 +	bool in_quote = false;
    1.32 +	for (; i < input_len; i++) {
    1.33 +		// Ignore quotes, except as an indicator as to whether to stop
    1.34 +		// at a space.
    1.35 +		if (input[i] == '"') {
    1.36 +			in_quote = !in_quote;
    1.37 +			continue;
    1.38 +		}
    1.39 +
    1.40 +		// If not in quotes, and we see a space, stop.
    1.41 +		if (isspace(input[i]) && !in_quote)
    1.42 +			break;
    1.43 +
    1.44 +		if (size_t(bufpos - buffer) < buflen)
    1.45 +			*bufpos++ = input[i];
    1.46 +	}
    1.47 +
    1.48 +	*bufpos = '\0';
    1.49 +	return i;
    1.50 +}
    1.51 +
    1.52 +//added by BAILOPAN :(
    1.53  //Takes a string and breaks it into a 1st param and rest params
    1.54  //strbreak(String[], First[], FirstLen, Rest[], RestLen)
    1.55  static cell AMX_NATIVE_CALL strbreak(AMX *amx, cell *params)	/* 5 param */
    1.56  {
    1.57 -
    1.58  	int _len;
    1.59  	bool in_quote = false;
    1.60  	bool had_quotes = false;
    1.61 @@ -946,9 +989,9 @@
    1.62  
    1.63  	size_t len = (size_t)_len;
    1.64  
    1.65 -    while (isspace(string[i]) && i<len)
    1.66 +	while (isspace(string[i]) && i<len)
    1.67  		i++;
    1.68 -    beg = i;
    1.69 +	beg = i;
    1.70  	for (; i<len; i++)
    1.71  	{
    1.72  		if (string[i] == '"' && !in_quote)
    1.73 @@ -968,15 +1011,18 @@
    1.74  				const char *start = had_quotes ? &(string[beg+1]) : &(string[beg]);
    1.75  				size_t _end = had_quotes ? (i==len-1 ? 1 : 2) : 0;
    1.76  				size_t end = (pos - _end > (size_t)LeftMax) ? (size_t)LeftMax : pos - _end;
    1.77 -				size_t to_go = end-beg;
    1.78 -				if (end && to_go)
    1.79 -				{
    1.80 -					while (to_go--)
    1.81 -						*left++ = (cell)*start++;
    1.82 -				}
    1.83 -				*left = '\0';
    1.84 +				
    1.85 +				// If there is anything to copy, make sure we copy min(maxlen, slicelen).
    1.86 +				size_t copylen = end >= beg
    1.87 +				                 ? ((end - beg > size_t(LeftMax))
    1.88 +				                    ? size_t(LeftMax)
    1.89 +				                    : end - beg
    1.90 +				                   )
    1.91 +				                 : 0;
    1.92 +				set_amxstring(amx, params[2], start, copylen);
    1.93 +
    1.94  				end = (len-i+1 > (size_t)RightMax) ? (size_t)RightMax : len-i+1;
    1.95 -                if (end)
    1.96 +				if (end)
    1.97  				{
    1.98  					start = &(string[i]);
    1.99  					while (end--)
   1.100 @@ -1224,6 +1270,7 @@
   1.101  	{"replace",			replace},
   1.102  	{"setc",			setc},
   1.103  	{"strbreak",		strbreak},
   1.104 +	{"argparse",		argparse},
   1.105  	{"strtolower",		strtolower},
   1.106  	{"strtoupper",		strtoupper},
   1.107  	{"str_to_num",		strtonum},
     2.1 --- a/plugins/include/string.inc	Wed Jan 22 20:03:52 2014 -0600
     2.2 +++ b/plugins/include/string.inc	Mon Feb 10 00:10:27 2014 -0800
     2.3 @@ -223,16 +223,6 @@
     2.4   */
     2.5  native strtok2(const text[], left[], const llen, right[], const rlen, const token = ' ', const trim = 0);
     2.6  
     2.7 -/* Gets parameters from text one at a time
     2.8 -   It breaks a string into the first parameter and the rest of the parameters
     2.9 -   (A left side and right side of the string)
    2.10 -   Example: to split text: "^"This is^" the best year",
    2.11 -   strbreak(text, arg1, len1, arg2, len2)
    2.12 -   arg1="This is", arg2=the best year
    2.13 -   This is more useful than parse() because you can keep breaking
    2.14 -   any number of arguments */
    2.15 -native strbreak(const text[], Left[], leftLen, Right[], rightLen);
    2.16 -
    2.17  /* Strips spaces from the beginning and end of a string. */
    2.18  native trim(text[]);
    2.19  
    2.20 @@ -277,6 +267,58 @@
    2.21  	return sString[i] == 0 && i != 0;
    2.22  }
    2.23  
    2.24 +// Warning: this function is deprecated as it does not work properly. Use
    2.25 +// argparse() or argbreak().
    2.26 +native strbreak(const text[], Left[], leftLen, Right[], rightLen);
    2.27 +
    2.28 +/**
    2.29 + * Parses an argument string to find the first argument. You can use this to
    2.30 + * replace strbreak().
    2.31 + *
    2.32 + * You can use argparse() to break a string into all of its arguments:
    2.33 + *   new arg[N], pos;
    2.34 + *   while (true) {
    2.35 + *     pos = argparse(string, pos, arg, sizeof(arg) - 1);
    2.36 + *     if (pos == -1)
    2.37 + *       break;
    2.38 + *   }
    2.39 + *
    2.40 + * All initial whitespace is removed. Remaining characters are read until an
    2.41 + * argument separator is encountered. A separator is any whitespace not inside
    2.42 + * a double-quotation pair (i.e. "x b" is one argument). If only one quotation
    2.43 + * mark appears, argparse() acts as if one existed at the end of the string.
    2.44 + * Quotation marks are never written back, and do not act as separators. For
    2.45 + * example, "a""b""c" will return "abc". An empty quote pair ("") will count
    2.46 + * as an argument containing no characters.
    2.47 + *
    2.48 + * argparse() will write an empty string to argbuffer if no argument is found.
    2.49 + *
    2.50 + * @param text          String to tokenize.
    2.51 + * @param pos           Position to start parsing from.
    2.52 + * @param argbuffer     Buffer to store first argument.
    2.53 + * @param maxlen        Size of the buffer.
    2.54 + * @return              If no argument was found, -1 is returned. Otherwise,
    2.55 + *                      the index to the next position to parse from is
    2.56 + *                      returned. This might be the very end of the string.
    2.57 + */
    2.58 +native argparse(const text[], pos, argbuffer[], maxlen);
    2.59 +
    2.60 +/* Emulates strbreak() using argparse(). */
    2.61 +stock argbreak(const text[], left[], leftlen, right[], rightlen)
    2.62 +{
    2.63 +	new pos = argparse(text, 0, left, leftlen);
    2.64 +
    2.65 +	if (pos == -1)
    2.66 +		return -1;
    2.67 +
    2.68 +	new textlen = strlen(text);
    2.69 +	while (pos < textlen && isspace(text[pos]))
    2.70 +		pos++;
    2.71 +
    2.72 +	copy(right, rightlen, text[pos]);
    2.73 +	return pos;
    2.74 +}
    2.75 +
    2.76  /* It is basically strbreak but you have a delimiter that is more than one character in length.
    2.77     You pass the Input string, the Left output, the max length of the left output,
    2.78     the right output , the max right length, and then the delimiter string.
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/plugins/testsuite/strbreak.sma	Mon Feb 10 00:10:27 2014 -0800
     3.3 @@ -0,0 +1,339 @@
     3.4 +// vim: set ts=4 sw=4 tw=99 sts=4 noet ft=c:
     3.5 +#include <amxmodx>
     3.6 +#include <amxmisc>
     3.7 +
     3.8 +#pragma ctrlchar '\'
     3.9 +
    3.10 +public plugin_init()
    3.11 +{
    3.12 +	register_plugin("strbreak tests", "1.0", "BAILOPAN");
    3.13 +	register_concmd("test_strbreak", "test_strbreak", 0, "admin");
    3.14 +	register_concmd("test_argparse", "test_argparse", 0, "admin");
    3.15 +}
    3.16 +
    3.17 +new TestCount = 0;
    3.18 +new FailCount = 0;
    3.19 +
    3.20 +test_reset()
    3.21 +{
    3.22 +	TestCount = 0;
    3.23 +	FailCount = 0;
    3.24 +}
    3.25 +
    3.26 +test_numequal(a, b)
    3.27 +{
    3.28 +	TestCount++;
    3.29 +	if (a == b) {
    3.30 +		server_print("[%d] PASS /%d/ == /%d/", TestCount, a, b);
    3.31 +	} else {
    3.32 +		server_print("[%d] FAIL /%d/ == /%d/", TestCount, a, b);
    3.33 +		FailCount++;
    3.34 +	}
    3.35 +}
    3.36 +
    3.37 +test_equal(a[], b[])
    3.38 +{
    3.39 +	TestCount++;
    3.40 +	if (equal(a, b)) {
    3.41 +		server_print("[%d] PASS /%s/ == /%s/", TestCount, a, b);
    3.42 +	} else {
    3.43 +		server_print("[%d] FAIL /%s/ == /%s/", TestCount, a, b);
    3.44 +		FailCount++;
    3.45 +	}
    3.46 +}
    3.47 +
    3.48 +public test_strbreak(id)
    3.49 +{
    3.50 +	test_reset();
    3.51 +
    3.52 +	new left[8], right[8];
    3.53 +
    3.54 +	// Test sort of normal behavior for strbreak().
    3.55 +	strbreak("a b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
    3.56 +	test_equal(left, "a");
    3.57 +	test_equal(right, "b c");
    3.58 +
    3.59 +	strbreak("a", left, sizeof(left) - 1, right, sizeof(right) - 1);
    3.60 +	test_equal(left, "a");
    3.61 +	test_equal(right, "");
    3.62 +
    3.63 +	strbreak("a\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
    3.64 +	test_equal(left, "a\"");
    3.65 +	test_equal(right, "");
    3.66 +
    3.67 +	strbreak("\"a\" yy", left, sizeof(left) - 1, right, sizeof(right) - 1);
    3.68 +	test_equal(left, "a");
    3.69 +	test_equal(right, "yy");
    3.70 +
    3.71 +	strbreak("\"a x", left, sizeof(left) - 1, right, sizeof(right) - 1);
    3.72 +	test_equal(left, "\"a x");
    3.73 +	test_equal(right, "");
    3.74 +
    3.75 +	strbreak("a  b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
    3.76 +	test_equal(left, "a");
    3.77 +	test_equal(right, "b  c");
    3.78 +
    3.79 +	strbreak("\"a\"  b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
    3.80 +	test_equal(left, "a");
    3.81 +	test_equal(right, "b  c");
    3.82 +
    3.83 +	strbreak("a  \"b  c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
    3.84 +	test_equal(left, "a");
    3.85 +	test_equal(right, "\"b  c\"");
    3.86 +
    3.87 +	strbreak("a  q\"b  c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
    3.88 +	test_equal(left, "a");
    3.89 +	test_equal(right, "q\"b  c\"");
    3.90 +
    3.91 +	strbreak("q \"b  c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
    3.92 +	test_equal(left, "q");
    3.93 +	test_equal(right, "\"b  c\"");
    3.94 +
    3.95 +	strbreak("q \"b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
    3.96 +	test_equal(left, "q");
    3.97 +	test_equal(right, "\"b  c");
    3.98 +
    3.99 +	// strbreak() functionality starts degrading here, but we test this to
   3.100 +	// preserve bug-for-bug compatibility.
   3.101 +	strbreak("q\"b  c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.102 +	test_equal(left, "\"b  c");
   3.103 +	test_equal(right, "\"");
   3.104 +
   3.105 +	strbreak("\"a  \"a  \"b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.106 +	test_equal(left, "a  \"");
   3.107 +	test_equal(right, "\"b  c");
   3.108 +
   3.109 +	strbreak("\"a  \"a  b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.110 +	test_equal(left, "a  \"");
   3.111 +	test_equal(right, "b  c");
   3.112 +
   3.113 +	strbreak("\"a\"a  b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.114 +	test_equal(left, "a\"");
   3.115 +	test_equal(right, "b  c");
   3.116 +
   3.117 +	strbreak("\"a\" x", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.118 +	test_equal(left, "a\"");
   3.119 +	test_equal(right, "x");
   3.120 +
   3.121 +	strbreak("\"a\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.122 +	test_equal(left, "a");
   3.123 +	test_equal(right, "\"");
   3.124 +
   3.125 +	strbreak("\"a\"b", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.126 +	test_equal(left, "\"a\"b");
   3.127 +	test_equal(right, "");
   3.128 +
   3.129 +	strbreak("q\" b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.130 +	test_equal(left, "q\" b  c");
   3.131 +	test_equal(right, "");
   3.132 +
   3.133 +	// Test truncation.
   3.134 +	strbreak("123456789A 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.135 +	test_equal(left, "1234567");
   3.136 +	test_equal(right, "1234567");
   3.137 +
   3.138 +	strbreak("12345 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.139 +	test_equal(left, "12345");
   3.140 +	test_equal(right, "1234567");
   3.141 +
   3.142 +	strbreak("\"12345\" 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.143 +	test_equal(left, "12345");
   3.144 +	test_equal(right, "1234567");
   3.145 +
   3.146 +	strbreak("\"123456789A\" 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.147 +	test_equal(left, "1234567");
   3.148 +	test_equal(right, "1234567");
   3.149 +
   3.150 +	strbreak("\"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.151 +	test_equal(left, "1234567");
   3.152 +	test_equal(right, "1234");
   3.153 +
   3.154 +	strbreak("\"123456789A\"               1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.155 +	test_equal(left, "1234567");
   3.156 +	test_equal(right, "1234");
   3.157 +
   3.158 +	strbreak("123456789A               1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.159 +	test_equal(left, "1234567");
   3.160 +	test_equal(right, "1234");
   3.161 +
   3.162 +	strbreak("123456789A               123456778923", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.163 +	test_equal(left, "1234567");
   3.164 +	test_equal(right, "1234567");
   3.165 +
   3.166 +	// Bonkers - left-hand makes no sense. Does whitespace count toward the buffer?!
   3.167 +	strbreak("  123456789A               123456778923", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.168 +	test_equal(left, "12345");
   3.169 +	test_equal(right, "1234567");
   3.170 +
   3.171 +	strbreak("        \"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.172 +	test_equal(left, "");
   3.173 +	test_equal(right, "1234");
   3.174 +
   3.175 +	strbreak("     \"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.176 +	test_equal(left, "12");
   3.177 +	test_equal(right, "1234");
   3.178 +
   3.179 +	new text_cmd[7], cheater[64];
   3.180 +	strbreak("  a ", text_cmd, 1, cheater, 31);
   3.181 +	test_equal(text_cmd, "");
   3.182 +	test_equal(cheater, "");
   3.183 +
   3.184 +	server_print("%d/%d passed", TestCount - FailCount, TestCount);
   3.185 +}
   3.186 +
   3.187 +public test_argparse(id)
   3.188 +{
   3.189 +	test_reset();
   3.190 +
   3.191 +	new left[8], right[8];
   3.192 +
   3.193 +	// Tests for behavior that we expect to work.
   3.194 +	argbreak("a b c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.195 +	test_equal(left, "a");
   3.196 +	test_equal(right, "b c");
   3.197 +
   3.198 +	argbreak("a", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.199 +	test_equal(left, "a");
   3.200 +	test_equal(right, "");
   3.201 +
   3.202 +	argbreak("a\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.203 +	test_equal(left, "a");
   3.204 +	test_equal(right, "");
   3.205 +
   3.206 +	argbreak("\"a\" yy", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.207 +	test_equal(left, "a");
   3.208 +	test_equal(right, "yy");
   3.209 +
   3.210 +	argbreak("\"a x", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.211 +	test_equal(left, "a x");
   3.212 +	test_equal(right, "");
   3.213 +
   3.214 +	argbreak("a  b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.215 +	test_equal(left, "a");
   3.216 +	test_equal(right, "b  c");
   3.217 +
   3.218 +	argbreak("\"a\"  b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.219 +	test_equal(left, "a");
   3.220 +	test_equal(right, "b  c");
   3.221 +
   3.222 +	argbreak("a  \"b  c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.223 +	test_equal(left, "a");
   3.224 +	test_equal(right, "\"b  c\"");
   3.225 +
   3.226 +	argbreak("a  q\"b  c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.227 +	test_equal(left, "a");
   3.228 +	test_equal(right, "q\"b  c\"");
   3.229 +
   3.230 +	argbreak("q \"b  c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.231 +	test_equal(left, "q");
   3.232 +	test_equal(right, "\"b  c\"");
   3.233 +
   3.234 +	argbreak("q \"b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.235 +	test_equal(left, "q");
   3.236 +	test_equal(right, "\"b  c");
   3.237 +
   3.238 +	argbreak("q\"b  c\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.239 +	test_equal(left, "qb  c");
   3.240 +	test_equal(right, "");
   3.241 +
   3.242 +	argbreak("\"a  \"a  \"b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.243 +	test_equal(left, "a  a");
   3.244 +	test_equal(right, "\"b  c");
   3.245 +
   3.246 +	argbreak("\"a  \"a  b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.247 +	test_equal(left, "a  a");
   3.248 +	test_equal(right, "b  c");
   3.249 +
   3.250 +	argbreak("\"a\"a  b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.251 +	test_equal(left, "aa");
   3.252 +	test_equal(right, "b  c");
   3.253 +
   3.254 +	argbreak("\"a\" x", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.255 +	test_equal(left, "a");
   3.256 +	test_equal(right, "x");
   3.257 +
   3.258 +	argbreak("\"a\"", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.259 +	test_equal(left, "a");
   3.260 +	test_equal(right, "");
   3.261 +
   3.262 +	argbreak("\"a\"b", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.263 +	test_equal(left, "ab");
   3.264 +	test_equal(right, "");
   3.265 +
   3.266 +	argbreak("q\" b  c", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.267 +	test_equal(left, "q b  c");
   3.268 +	test_equal(right, "");
   3.269 +
   3.270 +	// Test truncation.
   3.271 +	argbreak("123456789A 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.272 +	test_equal(left, "1234567");
   3.273 +	test_equal(right, "1234567");
   3.274 +
   3.275 +	argbreak("12345 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.276 +	test_equal(left, "12345");
   3.277 +	test_equal(right, "1234567");
   3.278 +
   3.279 +	argbreak("\"12345\" 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.280 +	test_equal(left, "12345");
   3.281 +	test_equal(right, "1234567");
   3.282 +
   3.283 +	argbreak("\"123456789A\" 123456789ABCDE", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.284 +	test_equal(left, "1234567");
   3.285 +	test_equal(right, "1234567");
   3.286 +
   3.287 +	argbreak("\"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.288 +	test_equal(left, "1234567");
   3.289 +	test_equal(right, "1234");
   3.290 +
   3.291 +	argbreak("\"123456789A\"               1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.292 +	test_equal(left, "1234567");
   3.293 +	test_equal(right, "1234");
   3.294 +
   3.295 +	argbreak("123456789A               1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.296 +	test_equal(left, "1234567");
   3.297 +	test_equal(right, "1234");
   3.298 +
   3.299 +	argbreak("123456789A               123456778923", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.300 +	test_equal(left, "1234567");
   3.301 +	test_equal(right, "1234567");
   3.302 +
   3.303 +	argbreak("  123456789A               123456778923", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.304 +	test_equal(left, "1234567");
   3.305 +	test_equal(right, "1234567");
   3.306 +
   3.307 +	argbreak("        \"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.308 +	test_equal(left, "1234567");
   3.309 +	test_equal(right, "1234");
   3.310 +
   3.311 +	argbreak("     \"123456789A\" 1234", left, sizeof(left) - 1, right, sizeof(right) - 1);
   3.312 +	test_equal(left, "1234567");
   3.313 +	test_equal(right, "1234");
   3.314 +
   3.315 +	new text_cmd[7], cheater[64];
   3.316 +	argbreak("  a ", text_cmd, 1, cheater, 31);
   3.317 +	test_equal(text_cmd, "a");
   3.318 +	test_equal(cheater, "");
   3.319 +
   3.320 +	// Test no-finds.
   3.321 +	new pos = argparse("   \t\n \t", 0, left, sizeof(left));
   3.322 +	test_numequal(pos, -1);
   3.323 +	test_equal(left, "");
   3.324 +
   3.325 +	// Loop for good measure.
   3.326 +	new argv[4][10], argc = 0;
   3.327 +	new text[] = "a \"b\" cd\"e\" \t f\"     ";
   3.328 +	pos = 0;
   3.329 +	while (argc < 4) {
   3.330 +		pos = argparse(text, pos, argv[argc++], 10 - 1);
   3.331 +		if (pos == -1)
   3.332 +			break;
   3.333 +	}
   3.334 +	test_numequal(argc, 4);
   3.335 +	test_equal(argv[0], "a");
   3.336 +	test_equal(argv[1], "b");
   3.337 +	test_equal(argv[2], "cde");
   3.338 +	test_equal(argv[3], "f     ");
   3.339 +
   3.340 +	server_print("%d/%d passed", TestCount - FailCount, TestCount);
   3.341 +}
   3.342 +