xref: /aosp_15_r20/external/libwebsockets/minimal-examples/api-tests/api-test-lws_tokenize/main.c (revision 1c60b9aca93fdbc9b5f19b2d2194c91294b22281)
1 /*
2  * lws-api-test-lws_tokenize
3  *
4  * Written in 2010-2021 by Andy Green <[email protected]>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  * This demonstrates the most minimal http server you can make with lws.
10  *
11  * To keep it simple, it serves stuff from the subdirectory
12  * "./mount-origin" of the directory it was started in.
13  * You can change that by changing mount.origin below.
14  */
15 
16 #include <libwebsockets.h>
17 #include <string.h>
18 #include <stdio.h>
19 
20 struct expected {
21 	lws_tokenize_elem e;
22 	const char *value;
23 	size_t len;
24 };
25 
26 struct tests {
27 	const char *string;
28 	struct expected *exp;
29 	int count;
30 	int flags;
31 };
32 
33 struct expected expected1[] = {
34 			{ LWS_TOKZE_TOKEN,		"protocol-1", 10 },
35 		{ LWS_TOKZE_DELIMITER, ",", 1},
36 			{ LWS_TOKZE_TOKEN,		"protocol_2", 10 },
37 		{ LWS_TOKZE_DELIMITER, ",", 1},
38 			{ LWS_TOKZE_TOKEN,		"protocol3", 9 },
39 		{ LWS_TOKZE_ENDED, NULL, 0 },
40 	},
41 	expected2[] = {
42 		{ LWS_TOKZE_TOKEN_NAME_COLON,		"Accept-Language", 15 },
43 			{ LWS_TOKZE_TOKEN,		"fr-CH", 5 },
44 		{ LWS_TOKZE_DELIMITER,			",", 1 },
45 			{ LWS_TOKZE_TOKEN,		"fr", 2 },
46 			{ LWS_TOKZE_DELIMITER,		";", 1},
47 			{ LWS_TOKZE_TOKEN_NAME_EQUALS,	"q", 1 },
48 			{ LWS_TOKZE_FLOAT,		"0.9", 3 },
49 		{ LWS_TOKZE_DELIMITER,			",", 1 },
50 			{ LWS_TOKZE_TOKEN,		"en", 2 },
51 			{ LWS_TOKZE_DELIMITER,		";", 1},
52 			{ LWS_TOKZE_TOKEN_NAME_EQUALS,	"q", 1 },
53 			{ LWS_TOKZE_FLOAT,		"0.8", 3 },
54 		{ LWS_TOKZE_DELIMITER,			",", 1 },
55 			{ LWS_TOKZE_TOKEN,		"de", 2 },
56 			{ LWS_TOKZE_DELIMITER,		";", 1},
57 			{ LWS_TOKZE_TOKEN_NAME_EQUALS,	"q", 1 },
58 			{ LWS_TOKZE_FLOAT,		"0.7", 3 },
59 		{ LWS_TOKZE_DELIMITER, ",", 1 },
60 			{ LWS_TOKZE_DELIMITER,		"*", 1 },
61 			{ LWS_TOKZE_DELIMITER,		";", 1 },
62 			{ LWS_TOKZE_TOKEN_NAME_EQUALS,	"q", 1 },
63 			{ LWS_TOKZE_FLOAT,		"0.5", 3 },
64 		{ LWS_TOKZE_ENDED, NULL, 0 },
65 	},
66 	expected3[] = {
67 			{ LWS_TOKZE_TOKEN_NAME_EQUALS,	"quoted", 6 },
68 			{ LWS_TOKZE_QUOTED_STRING,	"things:", 7 },
69 		{ LWS_TOKZE_DELIMITER,			",", 1 },
70 			{ LWS_TOKZE_INTEGER,		"1234", 4 },
71 		{ LWS_TOKZE_ENDED, NULL, 0 },
72 	},
73 	expected4[] = {
74 		{ LWS_TOKZE_ERR_COMMA_LIST,		",", 1 },
75 	},
76 	expected5[] = {
77 			{ LWS_TOKZE_TOKEN,		"brokenlist2", 11 },
78 		{ LWS_TOKZE_DELIMITER, ",", 1 },
79 		{ LWS_TOKZE_ERR_COMMA_LIST,		",", 1 },
80 	},
81 	expected6[] = {
82 			{ LWS_TOKZE_TOKEN,		"brokenlist3", 11 },
83 		{ LWS_TOKZE_DELIMITER, ",", 1 },
84 		{ LWS_TOKZE_ERR_COMMA_LIST,		",", 1 },
85 
86 	},
87 	expected7[] = {
88 			{ LWS_TOKZE_TOKEN, "fr", 2 },
89 			{ LWS_TOKZE_DELIMITER, "-", 1 },
90 			{ LWS_TOKZE_TOKEN, "CH", 2 },
91 			{ LWS_TOKZE_DELIMITER, ",", 1 },
92 			{ LWS_TOKZE_TOKEN, "fr", 2 },
93 			{ LWS_TOKZE_DELIMITER, ";", 1 },
94 			{ LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 },
95 			{ LWS_TOKZE_FLOAT, "0.9", 3 },
96 			{ LWS_TOKZE_DELIMITER, ",", 1 },
97 			{ LWS_TOKZE_TOKEN, "en", 2 },
98 			{ LWS_TOKZE_DELIMITER, ";", 1 },
99 			{ LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 },
100 			{ LWS_TOKZE_FLOAT, "0.8", 3 },
101 			{ LWS_TOKZE_DELIMITER, ",", 1 },
102 			{ LWS_TOKZE_TOKEN, "de", 2 },
103 			{ LWS_TOKZE_DELIMITER, ";", 1 },
104 			{ LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 },
105 			{ LWS_TOKZE_FLOAT, "0.7", 3 },
106 			{ LWS_TOKZE_DELIMITER, ",", 1 },
107 			{ LWS_TOKZE_TOKEN, "*", 1 },
108 			{ LWS_TOKZE_DELIMITER, ";", 1 },
109 			{ LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 },
110 			{ LWS_TOKZE_FLOAT, "0.5", 3 },
111 			{ LWS_TOKZE_ENDED, "", 0 },
112 	},
113 	expected8[] = {
114 		{ LWS_TOKZE_TOKEN, "Οὐχὶ", 10 },
115 		{ LWS_TOKZE_TOKEN, "ταὐτὰ", 12 },
116 		{ LWS_TOKZE_TOKEN, "παρίσταταί", 22 },
117 		{ LWS_TOKZE_TOKEN, "μοι", 6 },
118 		{ LWS_TOKZE_TOKEN, "γιγνώσκειν", 21 },
119 		{ LWS_TOKZE_DELIMITER, ",", 1 },
120 		{ LWS_TOKZE_TOKEN, "ὦ", 3 },
121 		{ LWS_TOKZE_TOKEN, "ἄνδρες", 13 },
122 		{ LWS_TOKZE_TOKEN, "᾿Αθηναῖοι", 20 },
123 		{ LWS_TOKZE_DELIMITER, ",", 1 },
124 		{ LWS_TOKZE_TOKEN, "greek", 5 },
125 		{ LWS_TOKZE_ENDED, "", 0 },
126 	},
127 	expected9[] = {
128 		/*
129 		 *  because the tokenizer scans ahead for = aggregation,
130 		 * it finds the broken utf8 before reporting the token
131 		 */
132 		{ LWS_TOKZE_ERR_BROKEN_UTF8, "", 0 },
133 	},
134 	expected10[] = {
135 		{ LWS_TOKZE_TOKEN, "badutf8-2", 9 },
136 		{ LWS_TOKZE_TOKEN, "퟿", 3 },
137 		{ LWS_TOKZE_DELIMITER, ",", 1 },
138 		{ LWS_TOKZE_ERR_BROKEN_UTF8, "", 0 },
139 	},
140 	expected11[] = {
141 		{ LWS_TOKZE_TOKEN, "1.myserver", 10 },
142 		{ LWS_TOKZE_DELIMITER, ".", 1 },
143 		{ LWS_TOKZE_TOKEN, "com", 3 },
144 		{ LWS_TOKZE_ENDED, "", 0 },
145 	},
146 	expected12[] = {
147 		{ LWS_TOKZE_TOKEN, "1.myserver.com", 14 },
148 		{ LWS_TOKZE_ENDED, "", 0 },
149 	},
150 	expected13[] = {
151 		{ LWS_TOKZE_TOKEN, "1.myserver.com", 14 },
152 		{ LWS_TOKZE_ENDED, "", 0 },
153 	},
154 	expected14[] = {
155 		{ LWS_TOKZE_INTEGER, "1", 1 },
156 		{ LWS_TOKZE_DELIMITER, ".", 1 },
157 		{ LWS_TOKZE_TOKEN, "myserver", 8 },
158 		{ LWS_TOKZE_DELIMITER, ".", 1 },
159 		{ LWS_TOKZE_TOKEN, "com", 3 },
160 		{ LWS_TOKZE_ENDED, "", 0 },
161 	},
162 	expected15[] = {
163 		{ LWS_TOKZE_TOKEN, "close", 5 },
164 		{ LWS_TOKZE_DELIMITER, ",", 1 },
165 		{ LWS_TOKZE_TOKEN, "Upgrade", 7 },
166 		{ LWS_TOKZE_ENDED, "", 0 },
167 	},
168 	expected16[] = {
169 		{ LWS_TOKZE_TOKEN_NAME_EQUALS, "a", 1 },
170 		{ LWS_TOKZE_TOKEN, "5", 1 },
171 		{ LWS_TOKZE_ENDED, "", 0 },
172 	},
173 	expected17[] = {
174 		{ LWS_TOKZE_TOKEN, "hello", 5 },
175 		{ LWS_TOKZE_ENDED, "", 0 },
176 	},
177 	expected18[] = {
178 		{ LWS_TOKZE_TOKEN, "x=y", 3 },
179 		{ LWS_TOKZE_ENDED, "", 0 },
180 	}
181 ;
182 
183 struct tests tests[] = {
184 	{
185 		" protocol-1, protocol_2\t,\tprotocol3\n",
186 		expected1, LWS_ARRAY_SIZE(expected1),
187 		LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_AGG_COLON
188 	}, {
189 		"Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
190 		expected2, LWS_ARRAY_SIZE(expected2),
191 		LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_AGG_COLON
192 	}, {
193 		"quoted = \"things:\", 1234",
194 		expected3, LWS_ARRAY_SIZE(expected3),
195 		LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_AGG_COLON
196 	}, {
197 		", brokenlist1",
198 		expected4, LWS_ARRAY_SIZE(expected4),
199 		LWS_TOKENIZE_F_COMMA_SEP_LIST
200 	}, {
201 		"brokenlist2,,",
202 		expected5, LWS_ARRAY_SIZE(expected5),
203 		LWS_TOKENIZE_F_COMMA_SEP_LIST
204 	}, {
205 		"brokenlist3,",
206 		expected6, LWS_ARRAY_SIZE(expected6),
207 		LWS_TOKENIZE_F_COMMA_SEP_LIST
208 	}, {
209 		"fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
210 		expected7, LWS_ARRAY_SIZE(expected7),
211 		LWS_TOKENIZE_F_ASTERISK_NONTERM | LWS_TOKENIZE_F_RFC7230_DELIMS
212 	},
213 	{
214 		" Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι, greek",
215 		expected8, LWS_ARRAY_SIZE(expected8),
216 		LWS_TOKENIZE_F_RFC7230_DELIMS
217 	},
218 	{
219 		"badutf8-1 \x80...",
220 		expected9, LWS_ARRAY_SIZE(expected9),
221 		LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_RFC7230_DELIMS
222 	},
223 	{
224 		"badutf8-2 \xed\x9f\xbf,\x80...",
225 		expected10, LWS_ARRAY_SIZE(expected10),
226 		LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_RFC7230_DELIMS
227 	},
228 	{
229 		"1.myserver.com",
230 		expected11, LWS_ARRAY_SIZE(expected11),
231 		0
232 	},
233 	{
234 		"1.myserver.com",
235 		expected12, LWS_ARRAY_SIZE(expected12),
236 		LWS_TOKENIZE_F_DOT_NONTERM
237 	},
238 	{
239 		"1.myserver.com",
240 		expected13, LWS_ARRAY_SIZE(expected13),
241 		LWS_TOKENIZE_F_DOT_NONTERM | LWS_TOKENIZE_F_NO_FLOATS
242 	},
243 	{
244 		"1.myserver.com",
245 		expected14, LWS_ARRAY_SIZE(expected14),
246 		LWS_TOKENIZE_F_NO_FLOATS
247 	},
248 	{
249 		"close,  Upgrade",
250 		expected15, LWS_ARRAY_SIZE(expected15),
251 		LWS_TOKENIZE_F_COMMA_SEP_LIST
252 	},
253 	{
254 		"a=5", expected16, LWS_ARRAY_SIZE(expected16),
255 		LWS_TOKENIZE_F_NO_INTEGERS
256 	},
257 	{
258 		"# comment1\r\nhello #comment2\r\n#comment3", expected17,
259 		LWS_ARRAY_SIZE(expected17), LWS_TOKENIZE_F_HASH_COMMENT
260 	},
261 	{
262 		"x=y", expected18,
263 		LWS_ARRAY_SIZE(expected18), LWS_TOKENIZE_F_EQUALS_NONTERM
264 	}
265 };
266 
267 /*
268  * add LWS_TOKZE_ERRS to the element index (which may be negative by that
269  * amount) to index this array
270  */
271 
272 static const char *element_names[] = {
273 	"LWS_TOKZE_ERR_BROKEN_UTF8",
274 	"LWS_TOKZE_ERR_UNTERM_STRING",
275 	"LWS_TOKZE_ERR_MALFORMED_FLOAT",
276 	"LWS_TOKZE_ERR_NUM_ON_LHS",
277 	"LWS_TOKZE_ERR_COMMA_LIST",
278 	"LWS_TOKZE_ENDED",
279 	"LWS_TOKZE_DELIMITER",
280 	"LWS_TOKZE_TOKEN",
281 	"LWS_TOKZE_INTEGER",
282 	"LWS_TOKZE_FLOAT",
283 	"LWS_TOKZE_TOKEN_NAME_EQUALS",
284 	"LWS_TOKZE_TOKEN_NAME_COLON",
285 	"LWS_TOKZE_QUOTED_STRING",
286 };
287 
288 
289 int
exp_cb1(void * priv,const char * name,char * out,size_t * pos,size_t olen,size_t * exp_ofs)290 exp_cb1(void *priv, const char *name, char *out, size_t *pos, size_t olen,
291 	size_t *exp_ofs)
292 {
293 	const char *replace = NULL;
294 	size_t total, budget;
295 
296 	if (!strcmp(name, "test")) {
297 		replace = "replacement_string";
298 		total = strlen(replace);
299 		goto expand;
300 	}
301 
302 	return LSTRX_FATAL_NAME_UNKNOWN;
303 
304 expand:
305 	budget = olen - *pos;
306 	total -= *exp_ofs;
307 	if (total < budget)
308 		budget = total;
309 
310 	if (out)
311 		memcpy(out + *pos, replace + (*exp_ofs), budget);
312 	*exp_ofs += budget;
313 	*pos += budget;
314 
315 	if (budget == total)
316 		return LSTRX_DONE;
317 
318 	return LSTRX_FILLED_OUT;
319 }
320 
321 static const char *exp_inp1 = "this-is-a-${test}-for-strexp";
322 
main(int argc,const char ** argv)323 int main(int argc, const char **argv)
324 {
325 	struct lws_context_creation_info info;
326 	struct lws_context *cx;
327 	struct lws_tokenize ts;
328 	lws_tokenize_elem e;
329 	const char *p;
330 	int n, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
331 			/* for LLL_ verbosity above NOTICE to be built into lws,
332 			 * lws must have been configured and built with
333 			 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
334 			/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
335 			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
336 			/* | LLL_DEBUG */;
337 	int fail = 0, ok = 0, flags = 0;
338 	char dotstar[512];
339 
340 	if ((p = lws_cmdline_option(argc, argv, "-d")))
341 		logs = atoi(p);
342 
343 	lws_set_log_level(logs, NULL);
344 	lwsl_user("LWS API selftest: lws_tokenize\n");
345 
346 	if ((p = lws_cmdline_option(argc, argv, "-f")))
347 		flags = atoi(p);
348 
349 
350 	memset(&info, 0, sizeof info);
351 	info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
352 		       LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW;
353 
354 	/*
355 	 * since we know this lws context is only ever going to be used with
356 	 * one client wsis / fds / sockets at a time, let lws know it doesn't
357 	 * have to use the default allocations for fd tables up to ulimit -n.
358 	 * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we
359 	 * will use.
360 	 */
361 	info.fd_limit_per_thread = 1 + 1 + 1;
362 
363 #if 0
364 #if defined(LWS_WITH_MBEDTLS) || defined(USE_WOLFSSL)
365 	/*
366 	 * OpenSSL uses the system trust store.  mbedTLS has to be told which
367 	 * CA to trust explicitly.
368 	 */
369 	info.client_ssl_ca_filepath = "./warmcat.com.cer";
370 #endif
371 #endif
372 #if 0
373 	n = open("./warmcat.com.cer", O_RDONLY);
374 	if (n >= 0) {
375 		info.client_ssl_ca_mem_len = read(n, memcert, sizeof(memcert));
376 		info.client_ssl_ca_mem = memcert;
377 		close(n);
378 		n = 0;
379 		memcert[info.client_ssl_ca_mem_len++] = '\0';
380 	}
381 #endif
382 	cx = lws_create_context(&info);
383 
384 	/* lws_strexp */
385 
386 	{
387 		size_t in_len, used_in, used_out;
388 		lws_strexp_t exp;
389 		char obuf[128];
390 		const char *p;
391 
392 		obuf[0] = '\0';
393 		lws_strexp_init(&exp, NULL, exp_cb1, obuf, sizeof(obuf));
394 		n = lws_strexp_expand(&exp, exp_inp1, 28, &used_in, &used_out);
395 		if (n != LSTRX_DONE || used_in != 28 ||
396 		    strcmp(obuf, "this-is-a-replacement_string-for-strexp")) {
397 			lwsl_notice("%s: obuf %s\n", __func__, obuf);
398 			lwsl_err("%s: lws_strexp test 1 failed: %d\n", __func__, n);
399 
400 			return 1;
401 		}
402 
403 		/* as above, but don't generate output, just find the length */
404 
405 		lws_strexp_init(&exp, NULL, exp_cb1, NULL, (size_t)-1);
406 		n = lws_strexp_expand(&exp, exp_inp1, 28, &used_in, &used_out);
407 		if (n != LSTRX_DONE || used_in != 28 || used_out != 39) {
408 			lwsl_err("%s: lws_strexp test 2 failed: %d, used_out: %d\n",
409 					__func__, n, (int)used_out);
410 
411 			return 1;
412 		}
413 
414 		p = exp_inp1;
415 		in_len = strlen(p);
416 		memset(obuf, 0, sizeof(obuf));
417 		lws_strexp_init(&exp, NULL, exp_cb1, obuf, 16);
418 		n = lws_strexp_expand(&exp, p, in_len, &used_in, &used_out);
419 		if (n != LSTRX_FILLED_OUT || used_in != 16 || used_out != 16) {
420 			lwsl_err("a\n");
421 			return 1;
422 		}
423 
424 		p += used_in;
425 		in_len -= used_in;
426 
427 		memset(obuf, 0, sizeof(obuf));
428 		lws_strexp_reset_out(&exp, obuf, 16);
429 
430 		n = lws_strexp_expand(&exp, p, in_len, &used_in, &used_out);
431 		if (n != LSTRX_FILLED_OUT || used_in != 5 || used_out != 16) {
432 			lwsl_err("b: n %d, used_in %d, used_out %d\n", n,
433 					(int)used_in, (int)used_out);
434 			return 2;
435 		}
436 
437 		p += used_in;
438 		in_len -= used_in;
439 
440 		memset(obuf, 0, sizeof(obuf));
441 		lws_strexp_reset_out(&exp, obuf, 16);
442 
443 		n = lws_strexp_expand(&exp, p, in_len, &used_in, &used_out);
444 		if (n != LSTRX_DONE || used_in != 7 || used_out != 7) {
445 			lwsl_err("c: n %d, used_in %d, used_out %d\n", n, (int)used_in, (int)used_out);
446 			return 2;
447 		}
448 	}
449 
450 	/* sanity check lws_strnncpy() */
451 
452 	lws_strnncpy(dotstar, "12345678", 4, sizeof(dotstar));
453 	if (strcmp(dotstar, "1234")) {
454 		lwsl_err("%s: lws_strnncpy check failed\n", __func__);
455 
456 		return 1;
457 	}
458 	lws_strnncpy(dotstar, "12345678", 8, 6);
459 	if (strcmp(dotstar, "12345")) {
460 		lwsl_err("%s: lws_strnncpy check failed\n", __func__);
461 
462 		return 1;
463 	}
464 
465 	/* sanity check lws_nstrstr() */
466 
467 	{
468 		static const char *t1 = "abc123456";
469 		const char *mcp;
470 
471 		mcp = lws_nstrstr(t1, strlen(t1), "abc", 3);
472 		if (mcp != t1) {
473 			lwsl_err("%s: lws_nstrstr 1 failed\n", __func__);
474 			return 1;
475 		}
476 		mcp = lws_nstrstr(t1, strlen(t1), "def", 3);
477 		if (mcp != NULL) {
478 			lwsl_err("%s: lws_nstrstr 2 failed\n", __func__);
479 			return 1;
480 		}
481 		mcp = lws_nstrstr(t1, strlen(t1), "456", 3);
482 		if (mcp != t1 + 6) {
483 			lwsl_err("%s: lws_nstrstr 3 failed: %p\n", __func__, mcp);
484 			return 1;
485 		}
486 		mcp = lws_nstrstr(t1, strlen(t1), "1", 1);
487 		if (mcp != t1 + 3) {
488 			lwsl_err("%s: lws_nstrstr 4 failed\n", __func__);
489 			return 1;
490 		}
491 		mcp = lws_nstrstr(t1, strlen(t1), "abc1234567", 10);
492 		if (mcp != NULL) {
493 			lwsl_err("%s: lws_nstrstr 5 failed\n", __func__);
494 			return 1;
495 		}
496 	}
497 
498 	/* sanity check lws_json_simple_find() */
499 
500 	{
501 		static const char *t1 = "{\"myname1\":true,"
502 					 "\"myname2\":\"string\", "
503 					 "\"myname3\": 123}";
504 		size_t alen;
505 		const char *mcp;
506 
507 		mcp = lws_json_simple_find(t1, strlen(t1), "\"myname1\":", &alen);
508 		if (mcp != t1 + 11 || alen != 4) {
509 			lwsl_err("%s: lws_json_simple_find 1 failed: (%d) %s\n",
510 				 __func__, (int)alen, mcp);
511 			return 1;
512 		}
513 
514 		mcp = lws_json_simple_find(t1, strlen(t1), "\"myname2\":", &alen);
515 		if (mcp != t1 + 27 || alen != 6) {
516 			lwsl_err("%s: lws_json_simple_find 2 failed\n", __func__);
517 			return 1;
518 		}
519 
520 		mcp = lws_json_simple_find(t1, strlen(t1), "\"myname3\":", &alen);
521 		if (mcp != t1 + 47 || alen != 3) {
522 			lwsl_err("%s: lws_json_simple_find 3 failed\n", __func__);
523 			return 1;
524 		}
525 
526 		mcp = lws_json_simple_find(t1, strlen(t1), "\"nope\":", &alen);
527 		if (mcp != NULL) {
528 			lwsl_err("%s: lws_json_simple_find 4 failed\n", __func__);
529 			return 1;
530 		}
531 	}
532 
533 	p = lws_cmdline_option(argc, argv, "-s");
534 
535 	for (n = 0; n < (int)LWS_ARRAY_SIZE(tests); n++) {
536 		int m = 0, in_fail = fail;
537 		struct expected *exp = tests[n].exp;
538 
539 		memset(&ts, 0, sizeof(ts));
540 		ts.start = tests[n].string;
541 		ts.len = strlen(ts.start);
542 		ts.flags = (uint16_t)tests[n].flags;
543 
544 		do {
545 			e = lws_tokenize(&ts);
546 
547 			lws_strnncpy(dotstar, ts.token, ts.token_len,
548 				     sizeof(dotstar));
549 			lwsl_info("{ %s, \"%s\", %d }\n",
550 				  element_names[e + LWS_TOKZE_ERRS], dotstar,
551 				  (int)ts.token_len);
552 
553 			if (m == (int)tests[n].count) {
554 				lwsl_notice("fail: expected end earlier\n");
555 				fail++;
556 				break;
557 			}
558 
559 			if (e != exp->e) {
560 				lwsl_notice("fail... tok %s vs expected %s\n",
561 					element_names[e + LWS_TOKZE_ERRS],
562 					element_names[exp->e + LWS_TOKZE_ERRS]);
563 				fail++;
564 				break;
565 			}
566 
567 			if (e > 0 &&
568 			    (ts.token_len != exp->len ||
569 			     memcmp(exp->value, ts.token, exp->len))) {
570 				lws_strnncpy(dotstar, ts.token, ts.token_len,
571 					     sizeof(dotstar));
572 				lwsl_notice("fail token mismatch %d %d %s\n",
573 					    (int)ts.token_len, (int)exp->len,
574 					    dotstar);
575 				fail++;
576 				break;
577 			}
578 
579 			m++;
580 			exp++;
581 
582 		} while (e > 0);
583 
584 		if (fail == in_fail)
585 			ok++;
586 	}
587 
588 	if (p) {
589 		ts.start = p;
590 		ts.len = strlen(p);
591 		ts.flags = (uint16_t)flags;
592 
593 		printf("\t{\n\t\t\"%s\",\n"
594 		       "\t\texpected%d, LWS_ARRAY_SIZE(expected%d),\n\t\t",
595 		       p, (int)LWS_ARRAY_SIZE(tests) + 1,
596 		       (int)LWS_ARRAY_SIZE(tests) + 1);
597 
598 		if (!flags)
599 			printf("0\n\t},\n");
600 		else {
601 			if (flags & LWS_TOKENIZE_F_MINUS_NONTERM)
602 				printf("LWS_TOKENIZE_F_MINUS_NONTERM");
603 			if (flags & LWS_TOKENIZE_F_AGG_COLON) {
604 				if (flags & 1)
605 					printf(" | ");
606 				printf("LWS_TOKENIZE_F_AGG_COLON");
607 			}
608 			if (flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) {
609 				if (flags & 3)
610 					printf(" | ");
611 				printf("LWS_TOKENIZE_F_COMMA_SEP_LIST");
612 			}
613 			if (flags & LWS_TOKENIZE_F_RFC7230_DELIMS) {
614 				if (flags & 7)
615 					printf(" | ");
616 				printf("LWS_TOKENIZE_F_RFC7230_DELIMS");
617 			}
618 			if (flags & LWS_TOKENIZE_F_DOT_NONTERM) {
619 				if (flags & 15)
620 					printf(" | ");
621 				printf("LWS_TOKENIZE_F_DOT_NONTERM");
622 			}
623 			if (flags & LWS_TOKENIZE_F_NO_FLOATS) {
624 				if (flags & 31)
625 					printf(" | ");
626 				printf("LWS_TOKENIZE_F_NO_FLOATS");
627 			}
628 			printf("\n\t},\n");
629 		}
630 
631 		printf("\texpected%d[] = {\n", (int)LWS_ARRAY_SIZE(tests) + 1);
632 
633 		do {
634 			e = lws_tokenize(&ts);
635 
636 			lws_strnncpy(dotstar, ts.token, ts.token_len,
637 				     sizeof(dotstar));
638 
639 			printf("\t\t{ %s, \"%s\", %d },\n",
640 				  element_names[e + LWS_TOKZE_ERRS],
641 				  dotstar, (int)ts.token_len);
642 
643 		} while (e > 0);
644 
645 		printf("\t}\n");
646 	}
647 
648 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
649 	{
650 		time_t t;
651 
652 		if (lws_http_date_parse_unix("Tue, 15 Nov 1994 08:12:31 GMT", 29, &t)) {
653 			lwsl_err("%s: date parse failed\n", __func__);
654 			fail++;
655 		} else {
656 			/* lwsl_notice("%s: %llu\n", __func__, (unsigned long long)t); */
657 			if (t != (time_t)784887151) {
658 				lwsl_err("%s: date parse wrong\n", __func__);
659 				fail++;
660 			} else {
661 				char s[30];
662 
663 				if (lws_http_date_render_from_unix(s, sizeof(s), &t)) {
664 					lwsl_err("%s: failed date render\n", __func__);
665 					fail++;
666 				} else {
667 					if (!strcmp(s, "Tue, 15 Nov 1994 08:12:31 GMT")) {
668 						lwsl_err("%s: date render wrong\n", __func__);
669 						fail++;
670 					}
671 				}
672 			}
673 		}
674 	}
675 #endif
676 
677 	{
678 		char buf[24];
679 		int m;
680 
681 		m = lws_humanize(buf, sizeof(buf), 0, humanize_schema_si);
682 		if (m != 1 || strcmp(buf, "0")) {
683 			lwsl_user("%s: humanize 1 fail '%s' (%d)\n", __func__, buf, m);
684 			fail++;
685 		}
686 		m = lws_humanize(buf, sizeof(buf), 2, humanize_schema_si);
687 		if (m != 1 || strcmp(buf, "2")) {
688 			lwsl_user("%s: humanize 2 fail '%s' (%d)\n", __func__, buf, m);
689 			fail++;
690 		}
691 		m = lws_humanize(buf, sizeof(buf), 999, humanize_schema_si);
692 		if (m != 3 || strcmp(buf, "999")) {
693 			lwsl_user("%s: humanize 3 fail '%s' (%d)\n", __func__, buf, m);
694 			fail++;
695 		}
696 		m = lws_humanize(buf, sizeof(buf), 1000, humanize_schema_si);
697 		if (m != 4 || strcmp(buf, "1000")) {
698 			lwsl_user("%s: humanize 4 fail '%s' (%d)\n", __func__, buf, m);
699 			fail++;
700 		}
701 		m = lws_humanize(buf, sizeof(buf), 1024, humanize_schema_si);
702 		if (m != 7 || strcmp(buf, "1.000Ki")) {
703 			lwsl_user("%s: humanize 5 fail '%s' (%d)\n", __func__, buf, m);
704 			fail++;
705 		}
706 	}
707 
708 	if (lws_strcmp_wildcard("allied", 6, "allied", 6)) {
709 		lwsl_user("%s: wc 1 fail\n", __func__);
710 		fail++;
711 	}
712 	if (lws_strcmp_wildcard("a*", 2, "allied", 6)) {
713 		lwsl_user("%s: wc 2 fail\n", __func__);
714 		fail++;
715 	}
716 	if (lws_strcmp_wildcard("all*", 4, "allied", 6)) {
717 		lwsl_user("%s: wc 3 fail\n", __func__);
718 		fail++;
719 	}
720 	if (lws_strcmp_wildcard("all*d", 5, "allied", 6)) {
721 		lwsl_user("%s: wc 4 fail\n", __func__);
722 		fail++;
723 	}
724 	if (!lws_strcmp_wildcard("b*", 2, "allied", 6)) {
725 		lwsl_user("%s: wc 5 fail\n", __func__);
726 		fail++;
727 	}
728 	if (!lws_strcmp_wildcard("b*ed", 4, "allied", 6)) {
729 		lwsl_user("%s: wc 6 fail\n", __func__);
730 		fail++;
731 	}
732 	if (!lws_strcmp_wildcard("allie", 5, "allied", 6)) {
733 		lwsl_user("%s: wc 7 fail\n", __func__);
734 		fail++;
735 	}
736 	if (lws_strcmp_wildcard("allie*", 6, "allied", 6)) {
737 		lwsl_user("%s: wc 8 fail\n", __func__);
738 		fail++;
739 	}
740 	if (lws_strcmp_wildcard("*llie*", 6, "allied", 6)) {
741 		lwsl_user("%s: wc 9 fail\n", __func__);
742 		fail++;
743 	}
744 	if (lws_strcmp_wildcard("*llied", 6, "allied", 6)) {
745 		lwsl_user("%s: wc 10 fail\n", __func__);
746 		fail++;
747 	}
748 	if (!lws_strcmp_wildcard("*llie", 5, "allied", 6)) {
749 		lwsl_user("%s: wc 11 fail\n", __func__);
750 		fail++;
751 	}
752 	if (!lws_strcmp_wildcard("*nope", 5, "allied", 6)) {
753 		lwsl_user("%s: wc 12 fail\n", __func__);
754 		fail++;
755 	}
756 	if (lws_strcmp_wildcard("*li*", 4, "allied", 6)) {
757 		lwsl_user("%s: wc 13 fail\n", __func__);
758 		fail++;
759 	}
760 	if (lws_strcmp_wildcard("*", 1, "allied", 6)) {
761 		lwsl_user("%s: wc 14 fail\n", __func__);
762 		fail++;
763 	}
764 	if (lws_strcmp_wildcard("*abc*d", 6, "xxabyyabcdd", 11)) {
765 		lwsl_user("%s: wc 15 fail\n", __func__);
766 		fail++;
767 	}
768 	if (lws_strcmp_wildcard("ssproxy.n.cn.*", 14,
769 				"ssproxy.n.cn.failures", 21)) {
770 		lwsl_user("%s: wc 16 fail\n", __func__);
771 		fail++;
772 	}
773 
774 	lwsl_user("Completed: PASS: %d, FAIL: %d\n", ok, fail);
775 
776 	lws_context_destroy(cx);
777 
778 	return !(ok && !fail);
779 }
780