static char sccsid[]="%Z% %M% %I% %E% %U%";
/************************************************/
/*												*/
/*	akxslike.c									*/
/*												*/
/*			coded by A.Kobayashi 2010/06/01		*/
/*												*/
/************************************************/
#include "akxcommon.h"

#define AKX_LIKE_ESCAPE	'\\'

static int iNLike=0,iNextLike=0;
static tdtLikeMaskCtl tmaskctl[10];
static unsigned short *gusap=NULL;
static uchar escape_char=AKX_LIKE_ESCAPE;

#define AKX_LIKE_OFFS	0x5f

static uchar gusAKX_LIKE_DB_OFFS[2]={AKX_LIKE_OFFS,0x00};
static uchar gusAKX_LIKE_DB_UNDL[2]={AKX_LIKE_OFFS-1,'_'};
static uchar gusAKX_LIKE_DB_PARS[2]={AKX_LIKE_OFFS-1,'%'};
static uchar gusAKX_LIKE_DB_LPAR[2]={AKX_LIKE_OFFS-1,'['};
static uchar gusAKX_LIKE_DB_RPAR[2]={AKX_LIKE_OFFS-1,']'};
static uchar gusAKX_LIKE_DB_MINS[2]={AKX_LIKE_OFFS-1,'-'};
static uchar gusAKX_LIKE_DB_MASK[2]={0xff,0x00};
static ushort AKX_LIKE_DB_OFFS=0;
static ushort AKX_LIKE_DB_UNDL;
static ushort AKX_LIKE_DB_PARS;
static ushort AKX_LIKE_DB_LPAR;
static ushort AKX_LIKE_DB_RPAR;
static ushort AKX_LIKE_DB_MINS;
static ushort AKX_LIKE_DB_MASK;

/********************************************/
/*                                          */
/********************************************/
static void _set_define()
{
	memcpy(&AKX_LIKE_DB_OFFS,gusAKX_LIKE_DB_OFFS,2);
	memcpy(&AKX_LIKE_DB_UNDL,gusAKX_LIKE_DB_UNDL,2);
	memcpy(&AKX_LIKE_DB_PARS,gusAKX_LIKE_DB_PARS,2);
	memcpy(&AKX_LIKE_DB_LPAR,gusAKX_LIKE_DB_LPAR,2);
	memcpy(&AKX_LIKE_DB_RPAR,gusAKX_LIKE_DB_RPAR,2);
	memcpy(&AKX_LIKE_DB_MINS,gusAKX_LIKE_DB_MINS,2);
	memcpy(&AKX_LIKE_DB_MASK,gusAKX_LIKE_DB_MASK,2);
}

/********************************************/
/*                                          */
/********************************************/
static char *_realloc(p,old_len,new_len)
void *p;
int old_len,new_len;
{
	char *np;

	if (old_len<0 || new_len<=0) return NULL;
	if (!p) {
		if (np=Malloc(new_len)) {
			memset(np,0,new_len);
		}
	}
	else {
		if (old_len>=new_len) return (char *)p;
		if (np=Malloc(new_len)) {
			memcpy(np,p,old_len);
			memset(np+old_len,0,new_len-old_len);
			Free(p);
		}
	}
	return np;
}

static int _to_db_code(s,len,d,opt,escape0)
uchar *s;
int len;
ushort *d;
int opt;	/* 0x01:0 not casecmp/ 1:casecmp */
			/* 0x02:1 \ escape */
uchar escape0;
{
	int l=0,opt1,opt2,n;
	uchar c,cc[2],escape,*s0;
	ushort us,*d0;
	SSP_S ssp;

	s0 = s;
	d0 = d;
	opt1 = opt & 0x01;
	escape = escape0;
	ssp.attr[0] = escape;
	while (len > 0) {
		if (akxqiskanji(s)) {
			memcpy((char *)d,s,2);
			s++;
			len--;
		}
		else {
			c = *s;
			if (c) {
				cc[0] = AKX_LIKE_OFFS;
				if (escape) {
					if (c == escape) {
						ssp.sp = 0;
						akx_conv_yen1(s,len,&ssp);
						ssp.sp--;
						s   += ssp.sp;
						len -= ssp.sp;
						c = ssp.attr[1];
					}
					else if (c=='%' || c=='_' || c=='[' || c==']') cc[0]--;
				}
				cc[1] = opt1 ? akxcupper(c) : c;
				memcpy((char *)d,cc,2);
			}
			else *d = 0;
		}
		s++;
		len--;
		d++;
		l++;
	}
	*d = 0;
/*
printf("_to_db_code: s=[%s]\n",s0);
printf("_to_db_code: d=[%s]\n",d0);
*/
	return l;
}

static int mkmask(tpmaskctl0,uspat,lb)
tdtLikeMaskCtl *tpmaskctl0;
ushort *uspat;
int lb;
{
	tdtLikeMask *mask,*pmask,*pmask_n;
	int maxmask;
	int i,n,ltpn,kpt,lpar;
	uchar stp0,mpn0,und0,lpn0;
	ushort *ptn,*ptn_n,us;

	mask = tpmaskctl0->lmc_mask;
	n = 0;
	ltpn = 0;
	stp0 = 0;
	mpn0 = 1;
	und0 = 0;
	lpn0 = lpar = 0;
	for (i=0;i<lb;i++) {
		us = uspat[i];
		if (lpar) {
			if (us == AKX_LIKE_DB_RPAR) {
				if (i-(lpar-1) > 1) lpn0++;
				lpar = 0;
			}
		}
		else {
			if (us == AKX_LIKE_DB_LPAR) lpar = i + 1;
			else if (us == AKX_LIKE_DB_PARS) {
			/*	lpn0 = i - stp0;	*/
				ltpn += lpn0;
				mask[n].stp = stp0;
				mask[n].len = lpn0;
				mask[n].mpn = mpn0;
				mask[n].und = und0;
/*
printf("mkmask: n=%d  stp0=%d lpn0=%d mpn0=%d und0=%d\n",n,stp0,lpn0,mpn0,und0);
*/
				stp0 = i + 1;
				mpn0 = 3;
				und0 = 0;
				lpn0 = 0;
			/*	i++;	*/
				n++;
			}
			else {
				lpn0++;
				if (us==AKX_LIKE_DB_UNDL) und0++;
			}
		}
	}
	if (lpar && (i-(lpar-1)>1)) lpn0++;
	mask[n].stp = stp0;
/*	lpn0 = lb - stp0;	*/
	ltpn += lpn0;
	mask[n].len = lpn0;
	mask[n].mpn = mpn0 - 1;
	mask[n].und = und0;
/*
printf("mkmask: n=%d  stp0=%d lpn0=%d mpn0=%d und0=%d\n",n,stp0,lpn0,mpn0-1,und0);
*/
	n++;
	tpmaskctl0->lmc_nptn   = n;
	tpmaskctl0->lmc_slptn  = ltpn;
/*
printf("mkmask: iNLike=%d iNextLike=%d\n",iNLike,iNextLike);
*/
	if (iNLike<10) {
		i = iNLike++;
		ptn   = NULL;
		pmask = NULL;
	}
	else {
		i = iNextLike++;
		if (iNextLike>=10) iNextLike = 0;
		ptn   = tmaskctl[i].lmc_pat;
		pmask = tmaskctl[i].lmc_mask;
	}
	if (!(ptn_n=(ushort *)_realloc(ptn,0,(lb+1)*2))) {
		if (ptn) Free(ptn);
		tmaskctl[i].lmc_pat  = NULL;
		return -1;
	}
	if (!(pmask_n=(tdtLikeMask *)_realloc((char *)pmask,0,n*sizeof(tdtLikeMask)))) {
		if (pmask) Free(pmask);
		tmaskctl[i].lmc_mask = NULL;
		return -1;
	}
	strcpy((char *)ptn_n,(char *)uspat);
	memcpy(pmask_n,mask,n*sizeof(tdtLikeMask));
	tmaskctl[i].lmc_nptn   = n;
	tmaskctl[i].lmc_slptn  = ltpn;
	tmaskctl[i].lmc_pat = ptn_n;
	tmaskctl[i].lmc_mask = pmask_n;
	return i;
}

static int undcmp(ap,la,ptn,lptn,lptn1)
ushort *ap,*ptn;
int la,lptn,lptn1;
{
	ushort *pap,*pptn,usptn,uspap;
	int n,i,j,lpar,imatch;

	if (!lptn) return 0;
	n = la - lptn + 1;
	if (n <= 0) return -1;
	for (i=0;i<n;i++) {
/*
printf("undcmp: i=%d ap=%04x\n",i,*ap);
*/
		pap = ap;
		pptn = ptn;
		uspap = *pap;
		lpar = 0;
		for (j=0;j<lptn1;j++) {
			usptn = *pptn;
/*
printf("undcmp: j=%d lpar=%d uspap=%04x usptn=%04x\n",j,lpar,uspap,usptn);
*/
			if (lpar) {
				if (usptn == AKX_LIKE_DB_RPAR) {
					lpar = 0;
					if (!imatch) break;
					pap++;
					uspap = *pap;
				}
				else if (uspap == usptn) imatch = 1;
			}
			else {
				if (usptn == AKX_LIKE_DB_LPAR) {
					lpar = 1;
					imatch = 0;
				}
				else {
					if ((usptn != AKX_LIKE_DB_UNDL) && (uspap != usptn)) break;
					pap++;
					uspap = *pap;
				}
			}
			pptn++;
		}
		if (j>=lptn1) {
			if (!lpar || imatch) return i;
		}
		ap++;
	}
	return -1;
}

static int _pxlike_pos(plike,bp,lbp,opt,pos)
tdtLike *plike;
char *bp;
int  lbp,opt,pos[];
	/* pos[0]: start pos by byte */
	/* pos[1]: matched string len by byte */
	/* pos[2]: start pos by DBite */
	/* pos[3]: matched string len by DByte */
{
	tdtLikeMaskCtl tmaskctl0;
	tdtLikeMask mask[20],*pmask;
	int i,n,la,lb,ltpn,la0,ipos,len,ix,sp,offset,lpn1;
	uchar stp0,mpn0,und0,lpn0,escape,uc;
	ushort *ap0,*ptn,*api;
	ushort uspat[256];
	ushort *usap;

	if (!bp) return -1;
	if (lbp < 0) return -2;
	if (lbp > 255) return -3;
	if ((!lbp) || (lbp==1 && *bp=='%')) {
		if (pos) {
			pos[0] += pos[1];
			pos[1] = plike->lk_alen/2 - 1 - pos[0];
			pos[2] += pos[3];
			pos[3] = plike->lk_la - pos[2];
		}
		return 0;
	}

	if (pos) sp = pos[2] + pos[3];
	else sp = 0;
	la   = plike->lk_la - sp;
	if (la <= 0) return 1;
	usap = plike->lk_usap + sp;

	if (uc=plike->lk_escape) {
		if (uc == 0xff) escape = '\0';
		else escape = plike->lk_escape;
	}
	else escape = escape_char;

	lb = lbp;
	lb = _to_db_code(bp,lb,uspat,opt,escape);
	for (i=0;i<iNLike;i++) {
		ptn = tmaskctl[i].lmc_pat;
		if (ptn && tmaskctl[i].lmc_mask) {
			if (!strcmp((char *)uspat,(char *)ptn)) break;
		}
	}
	if (i>=iNLike) {
		tmaskctl0.lmc_nptn   = 20;
		tmaskctl0.lmc_mask = mask;
		i = mkmask(&tmaskctl0,uspat,lb);
		if (i<0) i = iNLike;
	}
	if (i<iNLike) {
		n     = tmaskctl[i].lmc_nptn;
		pmask = tmaskctl[i].lmc_mask;
		ltpn  = tmaskctl[i].lmc_slptn;
	}
	else {
		n     = tmaskctl0.lmc_nptn;
		pmask = tmaskctl0.lmc_mask;
		ltpn  = tmaskctl0.lmc_slptn;
	}
	if (!ltpn) {
		if (pos) {
			pos[0] += pos[1];
			pos[1] = plike->lk_alen/2 - 1 - pos[0];
			pos[2] += pos[3];
			pos[3] = la;
		}
		return 0;
	}
	api = usap;
	ix = len = ipos = 0;
	la -= ltpn;
	if (la < 0) return 1;
	for (i=0;i<n;i++) {
		offset = 0;
		lpn0 = pmask[i].len;
		if (pmask[i].mpn && lpn0==pmask[i].und) {
			ipos = 0;
		}
		else {
			la0 = la + lpn0;
			ap0 = api;
			ptn = uspat + pmask[i].stp;
			switch (pmask[i].mpn) {
				case 0:	/* 完全一致 */
						if (lpn0 != la0) return 1;
						break;
				case 1:	/* 前方一致 */
						la0 = lpn0;
						break;
				case 2:	/* 後方一致 */
						offset = la0 - lpn0;
						ap0 += offset;
						la0 = lpn0;
						break;
				case 3:	/* 中間一致 */
						break;
				default:
						return -11;
			}
		/*	ipos = undcmp(ap0,la0,ptn,lpn0);	*/
			if (i < n-1) lpn1 = pmask[i+1].stp - pmask[i].stp - 1;
			else lpn1 = lb - pmask[i].stp;
			ipos = undcmp(ap0,la0,ptn,lpn0,lpn1);
			if (ipos < 0) return 1;
		}
		ipos += offset;
		api += (ipos + lpn0);
		la -= ipos;	/* add 2006.8.31 Koba */
/*
printf("_pxlike_pos: ipos=%d lpn0=%d\n",ipos,lpn0);
*/
		if (pos) {
			if (!ix && lpn0) {
				pos[2] = ipos;
				ix++;
			}
			len += (ipos + lpn0);
/*
printf("_pxlike_pos: len=%d\n",len);
*/
		}
	}
	if (pos) {
		pos[3] = len;
		len = ipos = 0;
		for (i=0;i<pos[3];i++) {
			if ((usap[i] & AKX_LIKE_DB_MASK) == AKX_LIKE_DB_OFFS) n = 1;
			else n = 2;
			if (i < pos[2]) ipos += n;
			else len += n;
		}
		pos[0] += pos[1] + ipos;
		pos[1] = len;
		pos[3] -= pos[2];
		pos[2] += sp;
	}
	return 0;
}

int akxs_xlike_pos(ap,lap,bp,lbp,opt,pos)
char *ap,*bp;
int lap,lbp,opt,pos[];
{
	int ret;
	tdtLike tlike;

	if (!ap || !bp) return -1;
	if (lap<0 || lbp<0) return -2;
	if (lbp > 255) return -3;
	if ((!lbp) || (lbp==1 && *bp=='%')) {
		if (pos) {
			pos[0] = 0;
			pos[1] = lap;
			pos[2] = 0;
			pos[3] = akxqmlen(ap,lap);
		}
		return 0;
	}
	if (!lap && lbp>0) return 1;
	if (opt) opt = 0x01;

	if (!AKX_LIKE_DB_OFFS) _set_define();

	memset(&tlike,0,sizeof(tdtLike));
	tlike.lk_max_pat = 10;
	tlike.lk_option  = opt;
	tlike.lk_usap = gusap;
	tlike.lk_alen = 0;
	if (ret=akxs_xlike_set_line(&tlike,ap,lap)) return ret;
	if (pos) memset(pos,0,sizeof(int)*4);
	gusap = tlike.lk_usap;
	return _pxlike_pos(&tlike,bp,lbp,opt,pos);
}

int akxs_xlike(ap,lap,bp,lbp,opt)
char *ap,*bp;
int lap,lbp,opt;
{
	return akxs_xlike_pos(ap,lap,bp,lbp,opt,NULL);
}

int akxs_str_like(ap,bp)
char *ap;	/* source */
char *bp;	/* patern */
{
	if (!ap || !bp) return -1;
	if (!strcmp(bp,"%")) return 0;
	return akxs_xlike(ap,strlen(ap),bp,strlen(bp),0);
}

tdtLike *akxs_xlike_new(max_pat,opt,pos)
int max_pat;
int opt;	/* 0x01:0 not casecmp/ 1:casecmp */
int pos[];
{
	tdtLike *plike;

	if (!AKX_LIKE_DB_OFFS) _set_define();

	if (max_pat <= 0) max_pat = 10;
	if (!(plike=(tdtLike *)Malloc(sizeof(tdtLike)))) return NULL;
	memset(plike,0,sizeof(tdtLike));
	plike->lk_maskctl = (tdtLikeMaskCtl *)Malloc(sizeof(tdtLikeMaskCtl)*max_pat);
	if (!plike->lk_maskctl) {
		Free(plike);
		return NULL;
	}
	plike->lk_max_pat = max_pat;
	plike->lk_option = opt & 0x01;
	plike->lk_pos = pos;
	return plike;
}

int akxs_xlike_set_line(plike,s,len)
tdtLike *plike;
char *s;
int len;
{
	ushort *usap;
	int alen;

	if (!plike || !s) return -1;
	if (len < 0) return -2;
	alen = (len+1)*2;
	usap = (ushort *)_realloc(plike->lk_usap,plike->lk_alen,alen);
	if (!usap) return -4;
	plike->lk_usap = usap;
	plike->lk_alen = alen;
	plike->lk_la = _to_db_code(s,len,plike->lk_usap,plike->lk_option,0);
	return 0;
}

int akxs_pxlike(plike,bp,lbp)
tdtLike *plike;
char *bp;
int  lbp;
{
	if (!plike) return -1;
	return _pxlike_pos(plike,bp,lbp,plike->lk_option,plike->lk_pos);
}

int akxs_like_escape(escape)
uchar escape;
{
	uchar c;

	c = escape_char;
	if (escape != 0xff) escape_char = escape;
	return c;
}
