#include "lib/config.h"
#include "lib/assert.h"
#include "lib/jregex.h"
#include "lib/output.h"
#include "lib/xmalloc.h"
#if 0
 #include "lib/euc.h"
#endif

#ifdef NO_REGEX_STATS
# define STATS(stuff)             /* nothing */
#else
# define STATS(stuff)             { stuff;}
  struct regex_stats regex_stats;
#endif

volatile unsigned apply_regex_abort = 0; /* just for linking */
#define sizeof_array(array)	(sizeof(array)/sizeof(array[0]))

#define PASS 	1
#define FAIL 	(!PASS)

#define IA	REGCOMP_IGNORE_ALPHA_CASE
#define IK	REGCOMP_IGNORE_KANA_CASE
#define IC	REGCOMP_IGNORE_CASE

#define X (const unsigned char *)
#define L __LINE__,
struct {
    const unsigned short line;
    const unsigned char *pattern;
    const unsigned char *string;
    const unsigned char *musthave;
    const unsigned flags;
    int val;
} tests[] = {
 {L X"jeff",	X"foo",			X"jef",		0, FAIL },
 {L X"a",	X"a",			X"a",    	0, PASS },
 {L X"a",	X"z",			X"a",    	0, FAIL },
 {L X"3",       X"123",          	X"3",    	0, PASS },
 {L X"3",       X"456",          	X"3",    	0, FAIL },
 {L X"3",       X"123",          	X"3", 		0, PASS },
 {L X"[a]",	X"a",			X"a",    	0, PASS },
 {L X"[a]",	X"z",			X"a",    	0, FAIL },
 {L X"[3]",     X"123",          	X"3",    	0, PASS },
 {L X"[3]",     X"456",          	X"3",    	0, FAIL },
 {L X"[3]",     X"123",          	X"3", 		0, PASS },
 {L X"x37",     X"x39xxx373x37", 	X"x37",		0, PASS },
 {L X"x37",     X"x39xxx3.73x7", 	X"x37",		0, FAIL },
 {L X"^wow",	X"swownow",             X"wo",		0, FAIL },
 {L X"^wow",	X"wownwos",		X"wo",		0, PASS },
 {L X"xxx$",    X"xxxx ",               X"x",		0, FAIL },
 {L X"xxx$",    X"xxx 3xxx",            X"x",		0, PASS },
 {L X"<jeff",	X"jeffrey", 		X"jef",		0, PASS },
 {L X"<jeff",	X">>jeffrey<<", 	X"jef",		0, PASS },
 {L X"<jeff",	X"xjeffrey<<", 		X"jef",		0, FAIL },
 {L X"<jeff",	X"jef", 		X"jef",		0, FAIL },
 {L X"jeff>",	X"jeffrey",		X"jef",		0, FAIL },
 {L X"jeff>",	X" jeffrey",		X"jef",		0, FAIL },
 {L X"jeff>",	X" jeff-rey",		X"jef",		0, PASS },
 {L X"jeff>",	X" sljeffrey",		X"jef",		0, FAIL },
 {L X"jeff>",	X" sljeff",		X"jef",		0, PASS },
 {L X"<jeff>",	X"jeff",		X"jef",		0, PASS },
 {L X"<jeff>",	X"jeff,wow",		X"jef",		0, PASS },
 {L X"<jeff>",	X"->jeff",		X"jef",		0, PASS },
 {L X"<jeff>",	X" jeff ",		X"jef",		0, PASS },
 {L X"<jeff>",	X" jeffrey ",		X"jef",		0, FAIL },
 {L X"ab+c",	X"abc",			X"abc",		0, PASS },
 {L X"ab+c",	X"ac",			X"abc",		0, FAIL },
 {L X"ab+c",	X"abbbbabbc",		X"abc",		0, PASS },
 {L X"ab+c",	X"abbbbabbdc",		X"abc",		0, FAIL },
 {L X"ab+c",	X"abbbbbbc",		X"abc",		0, PASS },
 {L X"ab*c",	X"ab",			X"ac",		0, FAIL },
 {L X"ab*c",	X"abac",		X"ac",		0, PASS },
 {L X"ab*c",	X"abbc",		X"ac",		0, PASS },
 {L X"ab?c",	X"ac",			X"ac",		0, PASS },
 {L X"ab?c",	X"abc",			X"ac",		0, PASS },
 {L X"ab?c",	X"abbc",		X"ac",		0, FAIL },
 {L X"abaa",	X"aababaa",		X"ab",		0, PASS },
 {L X"abaa",	X"aababab",		X"ab",		0, FAIL },
 {L X"弔",	X"弔",			X"弔",		0, PASS },
 {L X"弔",	X"x弔",			X"弔",		0, PASS },
 {L X"x弔",	X"弔",			X"x弔",		0, FAIL },
 {L X"x弔",	X"x弔",			X"弔x",		0, PASS },
 {L X"弔",	X"つだ",		X"弔",		0, FAIL },
 {L X"弔",	X"xつだ",		X"弔",		0, FAIL },
 {L X"x弔",	X"xつだ",		X"弔x",		0, FAIL },
 {L X"<弔",	X"xつだ",		X"弔",		0, FAIL },
 {L X"<弔",	X"x弔",			X"弔",		0, PASS },
 {L X"弔>",	X"x弔",			X"弔",		0, PASS },
 {L X"弔>",	X"x弔x",		X"弔",		0, PASS },
 {L X"[弔]",	X"弔",			X"弔",		0, PASS },
 {L X"[弔]",	X"x弔",			X"弔",		0, PASS },
 {L X"x[弔]",	X"弔",			X"x弔",		0, FAIL },
 {L X"x[弔]",	X"x弔",			X"弔x",		0, PASS },
 {L X"[弔]",	X"つだ",		X"弔",		0, FAIL },
 {L X"[弔]",	X"xつだ",		X"弔",		0, FAIL },
 {L X"x[弔]",	X"xつだ",		X"弔x",		0, FAIL },
 {L X"<[弔]",	X"xつだ",		X"弔",		0, FAIL },
 {L X"<[弔]",	X"x弔",			X"弔",		0, PASS },
 {L X"[弔]>",	X"x弔",			X"弔",		0, PASS },
 {L X"[弔]>",	X"x弔x",		X"弔",		0, PASS },
 {L X"a.c",	X"ac",			X"ac",		0, FAIL },
 {L X"a.c",	X"abc",			X"ac",		0, PASS },
 {L X"a.c",	X"abac",		X"ac",		0, FAIL },
 {L X"a.c",	X"abaxc",		X"ac",		0, PASS },
 {L X"a.c",	X"aaaxxc",		X"ac",		0, FAIL },
 {L X"a.c",	X"aacac",		X"ac",		0, PASS },
 {L X"ab*bc",	X"abc",			X"abc",		0, PASS },
 {L X"a[b]*bc",	X"abc",			X"abc",		0, PASS },
 {L X"a.*b",	X"ab",			X"ab",		0, PASS },
 {L X"a.*b",	X"aab",			X"ab",		0, PASS },
 {L X"a.*b",	X"axxxxb",		X"ab",		0, PASS },
 {L X"a.*b",	X"aabcdefbcb",		X"ab",		0, PASS },
 {L X"<a.*b",	X"xaab",		X"ab",		0, FAIL },
 {L X"<a.*b",	X"axa,ab",		X"ab",		0, PASS },
 {L X"a+...b",	X"aaaab",		X"ab",		0, PASS },
 {L X"a+...b",	X"aaab",		X"ab",		0, FAIL },
 {L X"a+...b",	X"xxaaab",		X"ab",		0, FAIL },
 {L X"a+...b",	X"aaxxab",		X"ab",		0, PASS },
 {L X"[n]",	X"m",			X"n",		0, FAIL },
 {L X"[m]",	X"m",			X"m",		0, PASS },
 {L X"[^m]",	X"x",			X"",		0, PASS },
 {L X"[^m]",	X"m",			X"",		0, FAIL },
 {L X"[a-z]",	X"m",			X"",		0, PASS },
 {L X"[a-z]",	X"1",			X"",		0, FAIL },
 {L X"^[a-z]",	X" m",			X"",		0, FAIL },
 {L X"^[^a-z]",	X" m",			X"",		0, PASS },
 {L X"^[^a-z]",	X"m",			X"",		0, FAIL },
 {L X"[[]",	X"[",			X"[",		0, PASS },
 {L X"\\[",	X"[",			X"[",		0, PASS },
 {L X"[[]",	X"p",			X"[",		0, FAIL },
 {L X"[^[]",	X"[[[[",		X"",		0, FAIL },
 {L X"[^[]",	X"[[,[",		X"",		0, PASS },
 {L X"[^-]",	X"a",			X"",		0, PASS },
 {L X"[^-]",	X"-",			X"",		0, FAIL },
 {L X"[-]",	X"z-",			X"-",		0, PASS },
 {L X"[a-b]",	X"b",			X"",		0, PASS },
 {L X"abc[def]ghi",X"abcdefghabcgghi",	X"abcghi",	0, FAIL },
 {L X"abc[def]ghi",X"abcdefghabceghi",	X"abcghi",	0, PASS },

 {L X"[弔つだ]",	X"a",		X"",	0, FAIL },
 {L X"[弔わか]",	X"つださま",	X"",	0, FAIL },
 {L X"[弔わか]",	X"つだかま",	X"",	0, PASS },
 {L X"[弔つだ]+",	X"a",		X"",	0, FAIL },
 {L X"[弔わか]+",	X"つださま",	X"",	0, FAIL },
 {L X"[弔わか]+",	X"つだかま",	X"",	0, PASS },
 {L X"つ[弔わか]+",	X"つだかま",	X"つ",	0, FAIL },
 {L X"つ[弔わか]+",	X"つかま",	X"つ",	0, PASS },
 {L X"つ[弔わか]*",	X"つだかま",	X"つ",	0, PASS },
 {L X"つ[弔わか]*",	X"だかま",	X"つ",	0, FAIL },
 {L X"つ[弔わか]*ま",	X"つかま",	X"つま",	0, PASS },
 {L X"つ[^弔わか]*かま",X"つかま",	X"つまか",      0, PASS },
 {L X"つ[^弔わか]ま",	X"つかま",	X"つま",	0, FAIL },
 {L X"つ[^弔わか]+ま",	X"つかま",	X"つま",	0, FAIL },
 {L X"つ[^弔わか]ま",	X"つかわま",	X"つま",	0, FAIL },
 {L X"つ[^弔わか]+ま",	X"つかわま",	X"つま",	0, FAIL },
 {L X"つ[弔わか]+ま",	X"つかわま",	X"つま",	0, PASS },
 {L X"つ[弔わa-zか]+ま",X"つかわま",	X"つま",	0, PASS },
 {L X"つ[弔わa-zか]+ま",X"つmわま",	X"つま",	0, PASS },
 {L X"つ[弔わa-zか]+ま",X"つ0わま",	X"つま",	0, FAIL },
 {L X"つ[^弔わa-zか]+ま",X"つかわま",	X"つま",	0, FAIL },
 {L X"つ[^弔わa-zか]+ま",X"つmわま",	X"つま",	0, FAIL },
 {L X"つ[^弔わa-zか]+ま",X"つ0わま",	X"つま",	0, FAIL },
 {L X"つ[^弔わa-zか]+ま",X"つ0くま",	X"つま",	0, PASS },
 {L X"(hi?)*",		X"h",		X"",	        0, PASS },
 {L X"a(bd)c",		X"abdc",	X"abcd",	0, PASS },
 {L X"a(bd)c",		X"abc",		X"abcd",	0, FAIL },
 {L X"a(bd)+c",		X"abc",		X"abdc",	0, FAIL },
 {L X"a(bd)*c",		X"ac",		X"ac",	0, PASS },
 {L X"a(bd)*c",		X"abc",		X"ac",	0, FAIL },
 {L X"a(bd)?c",		X"abc",		X"ac",	0, FAIL },
 {L X"a(bd)?c",		X"ac",		X"ac",	0, PASS },
 {L X"a(bd)*b",		X"abd",		X"ab",	0, PASS },
 {L X"a(bd)*b",		X"abdbd",	X"ab",	0, PASS },
 {L X"a(bd)*c",		X"abdbdbbc",	X"ac",	0, FAIL },
 {L X"ab",		X"ab",		X"ab",	0, PASS },
 {L X"a()b",		X"ab",		X"ab",	0, PASS },
 {L X"ab",		X"ac",		X"ab",	0, FAIL },
 {L X"a()b",		X"ac",		X"ab",	0, FAIL },
 {L X".*a",             X"jkjk",        X"a",	0, FAIL },
 {L X".*a",             X"jdakj",       X"a",	0, PASS },
 {L X".*a>",            X"jdakj",       X"a",	0, FAIL },
 {L X".*a>",            X"jda",         X"a",	0, PASS },
 {L X"a.*",             X"jjfkd",       X"a",	0, FAIL },
 {L X"a.*",             X"djakd",	X"a",	0, PASS },
 {L X"<a.*",            X"djakd",	X"a",	0, FAIL },
 {L X"<a.*",            X"akd",		X"a",	0, PASS },
 {L X"<a.*>",           X"akd",		X"a",	0, PASS },
 {L X"x(ab(cd)?)+y",    X"xy",          X"xaby",	0, FAIL },
 {L X"x(ab(cd)?)+y",    X"xaby",        X"xaby",	0, PASS },
 {L X"x(ab(cd)?)+y",    X"xabcdy",      X"xaby",	0, PASS },
 {L X"x(ab(cd)?)+y",    X"xababy",      X"xaby",	0, PASS },
 {L X"x(ab(cd)?)+y",    X"xabcdaby",    X"xaby",	0, PASS },
 {L X"x(ab(cd)?)+y",    X"xabay",       X"xaby",	0, FAIL },
 {L X"x(ab(cd)?)+y",    X"xabcday",     X"xaby",	0, FAIL },
 {L X"x(ab(cd)?)+y",    X"xababay",     X"xaby",	0, FAIL },
 {L X"x(ab(cd)?)+y",    X"xabcdabay",   X"xaby",	0, FAIL },
 {L X"^(a(ab)*((ab)+b)*)*y", X"y",	X"y",	0, PASS },
 {L X"^(a(ab)*((ab)+b)*)*y", X"ay",	X"y",	0, PASS },
 {L X"^(a(ab)*((ab)+b)*)*y", X"aaby",		X"y",	0, PASS },
 {L X"^(a(ab)*((ab)+b)*)*y", X"aababy",		X"y",	0, PASS },
 {L X"^(a(ab)*((ab)+b)*)*y", X"abby",		X"y",	0, FAIL },
 {L X"^(a(ab)*((ab)+b)*)*y", X"ababy",		X"y",	0, FAIL },
 {L X"^(a(ab)*((ab)+b)*)*y", X"aabby",		X"y",	0, PASS },
 {L X"^(a(ab)*((ab)+b)*)*y", X"aabababy",	X"y",	0, PASS },
 {L X"^(a(ab)*((ab)+b)*)*y", X"aabababby",	X"y",	0, PASS },
 {L X"^(a(ab)*((ab)+b)*)*y", X"",		X"y",	0, FAIL },
 {L X"^(a(ab)*((ab)+b)*)*y", X"aaay",		X"y",	0, PASS },
 {L X"^(a(ab)*((ab)+b)*)*y", X"aababaay",	X"y",	0, PASS },
 {L X"^(a(ab)*((ab)+b)*)*y", X"aby",		X"y",	0, FAIL },
 {L X"^(a(ab)*((ab)+b)*)*y", X"abby",		X"y",	0, FAIL },
 {L X"^(a(ab)*((ab)+b)*)*y", X"aabby",		X"y",	0, PASS },
 {L X"^(a(ab)*((ab)+b)*)*y", X"aaababaabby",	X"y",	0, PASS },
 {L X"a|b",            	X"a",			X"",	0, PASS },
 {L X"a|b",            	X"b",			X"",	0, PASS },
 {L X"a|b",            	X"b",			X"",	0, PASS },
 {L X"^(a|b)$",        	X"abb",			X"",	0, FAIL },
 {L X"^a|b$",          	X"abb",			X"",	0, PASS },
 {L X"^a|(b$)",         X"abb",			X"",	0, PASS },
 {L X"^(b|a)+$",        X"abb",                 X"",    0, PASS },
 {L X"^xyz|abb|123$",   X"abb",                 X"",    0, PASS },
 {L X"^(xyz|abb|123)$", X"abb",                 X"",    0, PASS },
 {L X"^xyz|bb|(123$)",  X"abb",                 X"",    0, PASS },
 {L X"^(xyz|bb|(123$))",X"abb",                 X"",    0, FAIL },
 {L X"^a+|b$",         	X"aa",			X"",	0, PASS },
 {L X"^a+|b$",         	X"aab",			X"",	0, PASS },
 {L X"^(a+|b)$",       	X"aab",			X"",	0, FAIL },
 {L X"(^a)|(b$)",       X"ba",                  X"",    0, FAIL },
 {L X"^(a|b)+$",        X"ba",                  X"",    0, PASS },
 {L X"<((a*b+)+|(a+b*))>", X"b",		X"",	0, PASS },
 {L X"<((a*b+)+|(a+b*))>", X"a",		X"",	0, PASS },
 {L X"<((a*b+)+|(a+b*))>", X"aba",		X"",	0, FAIL },
 {L X"<((a*b+)+|(a+b*))>", X"bb",		X"",	0, PASS },
 {L X"<((a*b+)+|(a+b*))>", X"aab",		X"",	0, PASS },
 {L X"<((a*b+)+|(a+b*))>", X"aaba",		X"",	0, FAIL },
 {L X"<((a*b+)+|(a+b*))>", X"aabab",		X"",	0, PASS },
 {L X"<((a*b+)+|a+b*)>",X"b",			X"",	0, PASS },
 {L X"<((a*b+)+|a+b*)>",X"a",			X"",	0, PASS },
 {L X"<((a*b+)+|a+b*)>",X"aba",			X"",	0, FAIL },
 {L X"<((a*b+)+|a+b*)>",X"bb",			X"",	0, PASS },
 {L X"<((a*b+)+|a+b*)>",X"aab",			X"",	0, PASS },
 {L X"<((a*b+)+|a+b*)>",X"aaba",		X"",	0, FAIL },
 {L X"<((a*b+)+|a+b*)>",X"aabab",		X"",	0, PASS },
 {L X"ab|cd|ef|gh",     X"ab",                  X"",     0, PASS },
 {L X"ab|cd|ef|gh",     X"cd",                  X"",     0, PASS },
 {L X"ab|cd|ef|gh",     X"ef",                  X"",     0, PASS },
 {L X"ab|cd|ef|gh",     X"gh",                  X"",     0, PASS },
 {L X"ab|cd|ef|gh",     X"ac",                  X"",     0, FAIL },
 {L X"ab|cd|ef|gh",     X"ah",                  X"",     0, FAIL },
 {L X"ab|cd|ef|gh",     X"abdfh",               X"",     0, PASS },
 {L X"a(b|cd|ef|g)h",   X"ah",                  X"ah",   0, FAIL },
 {L X"a(b|cd|ef|g)h",   X"abh",                 X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)h",   X"acdh",                X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)h",   X"aefh",                X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)h",   X"agh",                 X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)*h",  X"ah",                  X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)*h",  X"abh",                 X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)*h",  X"acdh",                X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)*h",  X"aefh",                X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)*h",  X"agh",                 X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)*h",  X"abcdefgh",            X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)*h",  X"agcdefh",             X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)*h",  X"acdh",                X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)*h",  X"aefh",                X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)*h",  X"agh",                 X"ah",   0, PASS },
 {L X"a(b|cd|ef|g)*h",  X"agxh",                X"ah",   0, FAIL },
 {L X"ab|ac|ad|ae",     X"aaaa",                X"a",    0, FAIL },
 {L X"a", 		X"A",		 	X"a",	0, FAIL },
 {L X"a", 		X"A",		 	X"a",	IA, PASS },
 {L X"[a]", 		X"A",		 	X"a",	0, FAIL },
 {L X"[a]", 		X"A",		 	X"a",	IA, PASS },
 {L X"A", 		X"a",		 	X"A",	0,  FAIL },
 {L X"A", 		X"a",		 	X"a",	IA, PASS },
 {L X"[A]", 		X"a",		 	X"A",	0,  FAIL },
 {L X"[A]", 		X"a",		 	X"a",	IA, PASS },
 {L X"A", 		X"A",		 	X"A",	0,  PASS },
 {L X"A", 		X"A",		 	X"a",	IA, PASS },
 {L X"[a-z]", 		X"S",		 	X"",	0,  FAIL },
 {L X"[a-z]", 		X"T",		 	X"",	IA, PASS },
 {L X"わかる",		X"わかる",	X"わかる",	0, PASS },
 {L X"わかる",		X"ワカル",	X"わかる",	0, FAIL },
 {L X"わか[る]",	X"わかる",	X"わかる",	0, PASS },
 {L X"わか[る]",	X"ワカル",	X"わかる",	0, FAIL },
 {L X"わかる",		X"わかる",	X"わかる",	IK, PASS },
 {L X"わかる",		X"わカる",	X"わかる",	IK, PASS },
 {L X"わかる",		X"ワカル",	X"わかる",	IK, PASS },
 {L X"わ[か]る",	X"わかる",	X"わかる",	IK, PASS },
 {L X"わ[か]る",	X"わカる",	X"わかる",	IK, PASS },
 {L X"わ[か]る",	X"ワカル",	X"わかる",	IK, PASS },
 {L X"[わ]か[る]",	X"ワカル",	X"わかる",	IK, PASS },
 {L X"[わ][か][る]",	X"ワカル",	X"わかる",	IK, PASS },
 {L X"[ワ][カ][ル]",	X"ワカル",	X"わかる",	IK, PASS },
 {L X"[ワ][カ][ル]",	X"わかる",	X"わかる",	IK, PASS },
 {L X"[わかる]",	X"カ",			X"",	0, FAIL },
 {L X"[わかる]",	X"カ",		 	X"",	IK, PASS },
 {L X"[わかる]",	X"ぴ",		 	X"",	IK, FAIL },
 {L X"[abcわかる]",	X"A",		 	X"",	IC, PASS },
 {L X"[abcわかる]",	X"Z",		 	X"",	IC, FAIL },
 {L X"<[abcわかる]>",	X"Cわ",		 	X"",	IC, PASS },
 {L X"<[abcわかる]>",	X"Zわ",		 	X"",	IC, PASS },
 {L X"[^a-z]",		X"a",			X"",	0,  FAIL },
 {L X"[^a-z]",		X"1",			X"",	0,  PASS },
 {L X"[^a-z]",		X"め",			X"",	0,  PASS },
 {L X"[a-z]",		X"め",			X"",	0,  FAIL },
 {L X"[わかる]",	X"a",			X"",	0,  FAIL },
 {L X"[わかる]",	X"ふ",			X"",	0,  FAIL },
 {L X"[わかる]",	X"か",			X"",	0,  PASS },
 {L X"[^わかる]",	X"a",			X"",	0,  PASS },
 {L X"[^わかる]",	X"ふ",			X"",	0,  PASS },
 {L X"[^わかる]",	X"か",			X"",	0,  FAIL },
 {L X"[abcわかる]",	X"か",			X"",	0,  PASS },
 {L X"[abcわかる]",	X"b",			X"",	0,  PASS },
 {L X"[abcわかる]",	X"ほ",			X"",	0,  FAIL },
 {L X"[abcわかる]",	X"x",			X"",	0,  FAIL },
 {L X"[^abcわかる]",	X"か",			X"",	0,  FAIL },
 {L X"[^abcわかる]",	X"b",			X"",	0,  FAIL },
 {L X"[^abcわかる]",	X"ほ",			X"",	0,  PASS },
 {L X"[^abcわかる]",	X"x",			X"",	0,  PASS },
 {L X"\\d",		X"d",			X"",	0,  FAIL },
 {L X"\\d",		X"\\",			X"",	0,  FAIL },
 {L X"\\d",		X"3",			X"",	0,  PASS },
 {L X"\\d",		X"a",			X"",	0,  FAIL },
 {L X"\\w+",		X"@",			X"",	0,  FAIL },
 {L X"\\w+",		X"a",			X"",	0,  PASS },
 {L X"[\\d]",		X"d",			X"",	0,  FAIL },
 {L X"[\\d]",		X"\\",			X"",	0,  FAIL },
 {L X"[\\d]",		X"3",			X"",	0,  PASS },
 {L X"[\\d]",		X"a",			X"",	0,  FAIL },
 {L X"[\\w+]",		X"@",			X"",	0,  FAIL },
 {L X"[\\w+]",		X"a",			X"",	0,  PASS },
 {L X"ー",		X"カー",		X"ー",	0,  PASS },
 {L X"<ー",		X"カー",		X"ー",	0,  FAIL },
 {L X"ー>",		X"カー",		X"ー",	0,  PASS },
 {L X"<ー>",		X"カー",		X"ー",	0,  FAIL },
 {L X"ー",		X"カーか",		X"ー",	0,  PASS },
 {L X"<ー",		X"カーか",		X"ー",	0,  FAIL },
 {L X"ー>",		X"カーか",		X"ー",	0,  FAIL },
 {L X"<ー>",		X"カーか",		X"ー",	0,  FAIL },
 {L 0}
};





static int lists_equal(const unsigned char *A, const unsigned char *B)
{
    unsigned char *a = xmalloc(strlen((void*)A)+1);
    unsigned char *b = xmalloc(strlen((void*)B)+1);
    unsigned char *ina;
    unsigned char *bend = b + strlen((void*)B);
    unsigned retval = 1;

    strcpy((void*)a, (void*)A);
    strcpy((void*)b, (void*)B);

    for (ina = a; *ina; ina++)
    {
	unsigned char *inb;
	if (ina[0] & 0x80)
	{
	    for (inb = b; inb < bend; inb++)
	    {
		if (ina[0] == inb[0] && ina[1] == inb[1]) {
		    inb[0] = inb[1] = 0;
		    break;
		} else {
		    if (inb[0] & 0x80)
			inb++;
		}
	    }
	    if (inb >= bend) {
		retval = 0;
		break;
	    }
	    ina++;
	}
	else
	{
	    for (inb = b; inb < bend; inb++)
	    {
		if (ina[0] == inb[0]) {
		    inb[0] = 0;
		    break;
		} else {
		    if (inb[0] & 0x80)
			inb++;
		}
	    }
	    if (inb >= bend) {
		retval = 0;
		break;
	    }
	}
    }

    if (retval) 
    {
	unsigned char *inb;
	/* all chars in a in b. Now make sure b is clear */
	for (inb = b; inb < bend; inb++)
	    if (inb[0]) {
		retval = 0;
		break;
	    }
    }

    free(a);
    free(b);
    return retval;
}

static int passed = 0, failed = 0;

#ifndef NO_PAREN_INFO
static void do_paren_tests(void)
{
    regex_t buf;
    #define MAX_PARENS_TO_MATCH	10
    matched_paren_t my_parens[MAX_PARENS_TO_MATCH];
    static struct {
	const unsigned short line;
	const unsigned char *pattern;
	const unsigned char *string;
	const unsigned char *parens[MAX_PARENS_TO_MATCH];
    } test[] = {
     {L X"a", 		        X"a",		},
     {L X"(a)",		        X"a",		{X"a"}},
     {L X"(a?)",	        X"a",		{X"a"}},
     {L X"(a?)",	        X"b",		{X""}},
     {L X"(a)?",		X"a",		{X"a"}},
     {L X"(a)?",		X"b",		},
     {L X"(a*)",		X"a",		{X"a"}},
     {L X"(a*)",		X"aaa",		{X"aaa"}},
     {L X"(a*)",		X"aaaX",	{X"aaa"}},
     {L X"(a*)",		X"b",		{X""}},
     {L X"(a)*",		X"a",		{X"a"}},
     {L X"(a)*",		X"aaa",		{X"a"}},
     {L X"(a)*",		X"aaaX",	{X"a"}},
     {L X"(a)*",		X"b",		},
     {L X"(a+)",		X"a",		{X"a"}},
     {L X"(a+)",		X"aaa",		{X"aaa"}},
     {L X"(a+)",		X"aaaX",	{X"aaa"}},
     {L X"(a)+",		X"a",		{X"a"}},
     {L X"(a)+",		X"aaa",		{X"a"}},
     {L X"(a)+",		X"aaaX",	{X"a"}},
     {L X"(hi?)*",		X"h",		{X"h"}},
     {L X"hi", 		        X"hi",		},
     {L X"(hi)",		X"hi",		{X"hi"}},
     {L X"(hi?)",		X"hi",		{X"hi"}},
     {L X"(hi?)",		X"h",		{X"h"}},
     {L X"(hi?)?",		X"i",		},
     {L X"(hi)?",		X"hi",		{X"hi"}},
     {L X"(hi*)",		X"hi",		{X"hi"}},
     {L X"(hi*)",		X"hii",		{X"hii"}},
     {L X"(hi*)",		X"hiiX",	{X"hii"}},
     {L X"(hi*)*",		X"b",		},
     {L X"(hi)*",		X"hi",		{X"hi"}},
     {L X"(hi)*",		X"hihihi",	{X"hi"}},
     {L X"(hi)*",		X"hihihiX",	{X"hi"}},
     {L X"(hi+)",		X"hi",		{X"hi"}},
     {L X"(hi+)",		X"hii",		{X"hii"}},
     {L X"(hi+)",		X"hiiX",	{X"hii"}},
     {L X"(hi)+",		X"hi",		{X"hi"}},
     {L X"(hi)+",		X"hihihi",	{X"hi"}},
     {L X"(hi)+",		X"hihihiX",	{X"hi"}},
     {L X"a(b)(c)(d)e",	        X"abcde",	{X"b",X"c",X"d"}},
     {L X"a(b)(c)(d)e",	        X"abcdEabcde",	{X"b",X"c",X"d"}},
     {L X"((a)(b))",		X"acab",	{X"ab",X"a",X"b"}},
     {L X"a(b?)(c)d",		X"abcXacd",	{X"", X"c"}},
     {L X"(a(b?)(c)d)",	        X"abcXacd",	{X"acd", X"", X"c"}},
     {L X"(a(b)?(c)d)",	        X"abcXacd",	{X"acd", 0, X"c"}},
     {L X"((a(b)d)+)",	        X"abdaBx",	{X"abd", X"abd", X"b"}},
     {L X"((jeff)+)",		X"mrjeffjeffrey",{X"jeffjeff", X"jeff"}}, 
     {L X"(a)(b?)(c)", 	        X"abc",		{X"a", X"b", X"c"}},
     {L X"(a)(b?)(c)", 	        X"ac",		{X"a", X"", X"c"}},
     {L X"((a(b))+)",	        X"xaBaby",	{X"aBab", X"ab", X"b"}},
     {L X"((ab(cde))|(ab(cdX)))", X"abcde",	{X"abcde", X"abcde", X"cde"}},
     {L X"((ab(cde))|(ab(cdf)))", X"abcdf", {X"abcdf", 0,0, X"abcdf",X"cdf"}},
     {L X"((((a)b)|((a)c))+)",  X"abAC",    {X"abAC",X"AC",0,X"A",X"AC",X"A"}},
     {L X"(ab(cde)|ab(cdX))",	X"abcde",	{X"abcde", X"cde"}},
     {L X"(ab(cde)|ab(cdf))", 	X"abcdf",	{X"abcdf", 0, X"cdf"}},
     {L X"(((a)b|(a)c)+)",	X"abAC",	{X"abAC",X"AC",X"A",X"A"}},
     {L X"(a)\\1",		X"aa",		{X"a"}},
     {L X"((a))\\1",		X"aa",		{X"a", X"a"}},
     {L X"((a))\\2",		X"aA",		{X"a", X"a"}},
     {L X"(((a))\\2)",		X"aA",		{X"aA", X"a", X"a"}},
     {L X"(((a))\\3)",		X"aA",		{X"aA", X"a", X"a"}},
     {L X"^([a-c]*)\\1$",	X"abCABc",	{X"abC"}},
     {L X"(わかる)\\1", 	X"わかるワカル",{X"わかる"}},
     {L X"((([a-z])\\3)+(\\2))",X"aAbBBbcC",	{X"aAbBBb", X"bB", X"b", X"Bb"}},
    };
    int i, r, e;

    for (i = 0; i < sizeof_array(test); i++)
    {
	regexec_paren_info = my_parens;
	regexec_paren_info_size = MAX_PARENS_TO_MATCH;

	#if 0
	outputf("「%s」「%s」\n", test[i].pattern, test[i].string);
	#endif

	r = regcomp(&buf, test[i].pattern, REGCOMP_SAVE_MATCHED_PAREN_INFO |
		                           REGCOMP_IGNORE_CASE);

	if (r != REGCOMP_SUCCESS) {
	    outputf("paren test line %d recomp returns %d:\n",
		    test[i].line, i, r);
	    failed++;
	    continue;
	}
	regexec_setflags(0);
	r = regexec(&buf, test[i].string, strlen((void*)test[i].string));
	if (r == 0) {
	    outputf("PAREN TEST LINE %d REGEXEC FAILED:\n", test[i].line);
	    outputf("pattern 「%s」string 「%s」\n", test[i].pattern,
		   test[i].string);
	    regexec_setflags(REGEX_DEBUG);
	    regexec(&buf, test[i].string, strlen((void*)test[i].string));
	    failed++;
	    continue;
	}
	e = 0;
	for (r = 0; r < MAX_PARENS_TO_MATCH; r++)
	{
	    const unsigned char *want, *got;
	    int want_len, got_len;
	    want = test[i].parens[r];

	    got = (r >= regexec_paren_info_used) ? 0 :
		  (regexec_paren_info[r].match_start == 0 ||
		   regexec_paren_info[r].match_end   == 0) ? 0 :
		   regexec_paren_info[r].match_start;
	    if (want == 0 && got == 0)
		continue;
	    want_len = strlen((void*)want);
	    got_len = got == 0 ? 0 : (regexec_paren_info[r].match_end -
				      regexec_paren_info[r].match_start);
	    if (want == 0) {
		outputf("PAREN TEST LINE %d.%d: wanted undefined, got [%.*s]\n",
		       test[i].line, r, got_len, got);
		e++;
	    } else if (got == 0) {
		outputf("PAREN TEST LINE %d.%d: wanted [%s] got undefined\n",
		       test[i].line, r, want);
		e++;
	    }
	    else if (want_len == got_len && strncmp(want, got, want_len)==0)
	    {
		/* matches */
	    } else {
		outputf("PAREN TEST LINE %d.%d: wanted [%s] got [%.*s]\n",
		       test[i].line, r, want, got_len, got);
		e++;
	    }
	}
	if (e) {
	    outputf("pattern 「%s」string 「%s」\n", test[i].pattern,
		   test[i].string);
	    regexec_setflags(REGEX_DEBUG);
	    showregex(&buf);
	    regexec(&buf, test[i].string, strlen((void*)test[i].string));
	    for (r = 0; r < regexec_paren_info_used; r++)
	    {
		if (regexec_paren_info[r].match_start == 0&&
		    regexec_paren_info[r].match_end == 0)
		    outputf("  paren #%d undefined (s=0, e=0)\n", r);
		else if (regexec_paren_info[r].match_start == 0)
		    outputf("  paren #%d undefined (s=0)\n", r);
		else if (regexec_paren_info[r].match_end == 0)
		    outputf("  paren #%d undefined (e=0)\n", r);
		else
		    outputf("  paren #%d [%.*s]\n", r,
			   (int)(regexec_paren_info[r].match_end -
				 regexec_paren_info[r].match_start),
			   regexec_paren_info[r].match_start);
	    }
	    failed++;
	} else {
	    passed++;
	}
    }
}
#endif /* NO_PAREN_INFO */

/*
 * If no args, run all the standard tests.
 *
 * Otherwise, usage is:
 *        a.out PATTERN STRING [FLAGS]
 * (where FLAGS is a >decimal< number)
 * In this case, the PATTERN is compiled and matched against the STRING
 * with all debugging on.
 */
int main(int argc, char *argv[])
{
    const unsigned char *pattern = X"<wo?rd>"; /* default */
    const unsigned char *string  = X"word";    /* default */
    regex_t buf;
    unsigned flags = 0;
    unsigned regex_debug_flag = 0;
    int i;

    if (argc == 2 && strcmp(argv[1], "-help") == 0) {
	printf("%s        -- run built-in tests\n", argv[0]);
	printf("%s pattern string [flags [special]]\n", argv[0]);
	printf("special are: 1-debug, 2-POSIXish, 3-both\n");
	exit(0);
    }


    if (argc > 1)
    {
        #ifndef NO_PAREN_INFO
	matched_paren_t my_parens[MAX_PARENS_TO_MATCH];
	regexec_paren_info = my_parens;
	regexec_paren_info_size = MAX_PARENS_TO_MATCH;
        #endif
	if (argc > 1)
	    pattern = (const unsigned char *)argv[1];
	if (argc > 2)
	    string = (const unsigned char *)argv[2];
	if (argc > 3)
	    flags = atoi(argv[3]);

	if (argc > 4) {
	    switch (atoi(argv[4])) {
	      case 0: {
		break;
	      }
	      case 1:
		regex_debug_flag = REGEX_DEBUG;
		break;
	      case 2:
		regex_debug_flag = REGEXEC_LLM;
		break;
	      case 3:
		regex_debug_flag = REGEXEC_LLM|REGEX_DEBUG;
		break;
	    }
	}

	flags |= REGCOMP_SAVE_MATCHED_PAREN_INFO;

	if (regcomp(&buf, pattern, flags|REGEX_DEBUG) == 0)
	{
	    const unsigned char *orig_string = string;
	    int times = 0;
	    int overall_match = 0;
	    int overall_fail = 0;

	    showregex(&buf);
	    regexec_setflags(flags|regex_debug_flag);

	  retry:
	    times++;
	    regex_reset_stats();
	    i = regexec(&buf, string, strlen((void*)string));
	    if (!regex_debug_flag) {
		outputf(i ? "Regex Matches.\n" : "Regex Fails.\n");
	    }

	    overall_match += regex_stats.matches;
	    overall_fail  += regex_stats.failures;

	    STATS(
	      outputf("\n%d Tests. Cycles %d (%dm+%df=%dc: success = %.1f%%)\n",
		      regex_stats.tests,
		      regex_stats.cycles,
		      regex_stats.matches, regex_stats.failures,
		      regex_stats.matches+ regex_stats.failures,
		      regex_stats.matches * 100.0 /
		          (regex_stats.matches+ regex_stats.failures));

	      outputf("States: %d pushed, %d popped (max depth %d)\n",
		      regex_stats.states_pushed,
		      regex_stats.states_popped,
		      regex_stats.max_state_depth);

  	      if (regex_stats.parens_entered)
	           outputf("Parens: %d entered, %d saved, %d pushed, %d popped.\n",
		      regex_stats.parens_entered,
		      regex_stats.parens_saved,
		      regex_stats.parens_pushed,
		      regex_stats.parens_popped);
	    );

	    if (i) {
		const unsigned char *retry_point = 0;

		#ifndef NO_REGEXEC_MATCH_POINTS
		{
		    /* show the string and where it matched */
		    const unsigned char *ptr = orig_string;
		    outputf(">|%s|<\n>", orig_string);

		    while (ptr < string) {
			outchar('>');
			ptr++;
		    }
		    outchar('|');

		    while (ptr < regexec_match_start) {
			outchar(' ');
			ptr++;
		    }
		    if (regexec_match_start == regexec_match_end)
			outchar('\\');
		    else {
			while (ptr < regexec_match_end)
			{
			    outchar('^');
			    ptr++;
			}
		    }
                    while (*(ptr++))
			outchar(' ');
		    output("|<\n");

		    /* retry from the end of the match if any string left*/
		    if (*regexec_match_end) {
			retry_point = regexec_match_end;
			if (regexec_match_start == regexec_match_end)
			    retry_point++;
		    }
		}
		#endif /* NO_REGEXEC_MATCH_POINTS */
		#ifndef NO_PAREN_INFO
		{
		    for (i = 0; i < regexec_paren_info_used; i++)
		    {
			if (regexec_paren_info[i].match_start == 0&&
			    regexec_paren_info[i].match_end == 0)
			    outputf("  paren #%d undefined (s=0, e=0)\n", i);
			else if (regexec_paren_info[i].match_start == 0)
			    outputf("  paren #%d undefined (s=0)\n", i);
			else if (regexec_paren_info[i].match_end == 0)
			    outputf("  paren #%d undefined (e=0)\n", i);
			else
			    outputf("  paren #%d [%.*s]\n", i,
				   (int)(regexec_paren_info[i].match_end -
					 regexec_paren_info[i].match_start),
				   regexec_paren_info[i].match_start);
		    }
		}
                #endif /* NO_PAREN_INFO */

		if (retry_point) {
		    string = retry_point;
		    output("\n-------------------\n");
		    goto retry;
		}
	    }
	    if (times > 1) {
		printf("OVERALL: %d match + %d fail = %d tests\n",
		       overall_match, overall_fail, overall_match + overall_fail);
	    }
	}

    } else {
	int I, j;
	static unsigned extra_flags[] =
	{
	    REGCOMP_CALC_MUSTHAVE,
	    REGCOMP_CALC_MUSTHAVE|REGCOMP_JUST_MATCH,
	    #ifndef NO_PAREN_INFO
	    REGCOMP_CALC_MUSTHAVE|REGCOMP_SAVE_MATCHED_PAREN_INFO,
            #endif
	};

	for (I = 0; I < sizeof_array(extra_flags); I++)
	{
	    for (j = 0; tests[j].pattern; j++)
	    {

                #if 0
		outputf("「%s」「%s」 flags %d\n", tests[j].pattern,
		       tests[j].string, tests[j].flags|extra_flags[I]);
                #endif
		i = regcomp(&buf, tests[j].pattern,
			    tests[j].flags|extra_flags[I]);

		if (i != 0) {
		    outputf("LINE d: <REGCOMP RETURNS %d for __%s__, flags %x>\n",
			    tests[j].line,
			   i, tests[j].pattern, tests[j].flags|extra_flags[I]);
		    regcomp(&buf, tests[j].pattern,
			    tests[j].flags|REGEX_DEBUG|extra_flags[I]);
		    failed++;
 		} else if (!lists_equal(buf.musthave, tests[j].musthave)) {
		    outputf("LINE %d: for _%s_ got musthave=[%s], expected [%s]\n",
			    tests[j].line,
			   tests[j].pattern, buf.musthave, tests[j].musthave);
		    failed++;
		    regfree(&buf);
		} else {
		    regexec_setflags(tests[j].flags|extra_flags[I]);

		    i = regexec(&buf, tests[j].string,strlen((void*)tests[j].string));
				
		    if (i == tests[j].val)
			passed++;
		    else
		    {
			failed++;
			outputf("\n\nTEST LINE %d, EXPECTED TO %s「%s」「%s」[%x]<<\n",
				tests[j].line,
			       tests[j].val == PASS ? "PASS" : "FAIL",
			       tests[j].pattern, tests[j].string,
			       tests[j].flags|extra_flags[I]);
			regfree(&buf);

			regcomp(&buf, tests[j].pattern,
				extra_flags[I]|tests[j].flags|REGEX_DEBUG);
			regexec_setflags(tests[j].flags|REGEX_DEBUG|
					 extra_flags[I]);
			regexec(&buf,tests[j].string,strlen((void*)tests[j].string));
		    }
		    regfree(&buf);
		}
	    }
	}

	#ifndef NO_PAREN_INFO
	do_paren_tests();
	#endif
	outputf("\n%d tests passed, %d tests failed\n", passed, failed);
    }
    return 0;
}
