1/*
2 * lws-api-test-lws_tokenize
3 *
4 * Written in 2010-2021 by Andy Green <andy@warmcat.com>
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
20struct expected {
21	lws_tokenize_elem e;
22	const char *value;
23	size_t len;
24};
25
26struct tests {
27	const char *string;
28	struct expected *exp;
29	int count;
30	int flags;
31};
32
33struct 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
183struct 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
272static 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
289int
290exp_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
304expand:
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
321static const char *exp_inp1 = "this-is-a-${test}-for-strexp";
322
323int 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