#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <jfctype.h>
#include "defines.h"
#include "astdio.h"
#include "astring.h"
#include "acmp.h"
#include "acdata.h"

#define	hash(c)	((c&0xff)|(c>>7))
// ２バイトコードは>=$8000。故に上位バイトのbit7は常に1
// hash=256+1〜255
// 逆に１バイトコードは<=255だから上位バイトのbit7は常に0
// hash=1〜255
// 全角／半角共に0はEOSだから存在しない
#define	HSIZE	512
// 変更する時はhash()のc>>?も変更

typedef struct _GrepMACROs {
	unsint	ch;	// 文字コード
	unchar	*str;	// マクロ文字列
} GrepMACROs;

typedef struct _GrepMACRO {
	GrepMACROs	*gms;	// ワークエリアアドレス
	unchar		*strs;	// 文字列ワークエリア
	unsint		max;	// マクロ数
	unsint	htbl[HSIZE];	// ハッシュ表
	unsint		mode;	// 文字判定モード
} GrepMACRO;
static GrepMACRO gm;

static unchar *SrchGrepMACRO(unsint c)
// cに対応したマクロ文字列を設定する
{
int i;
unsint h;
	if (gm.mode) {
		// 英大文字小文字全角半角、半角片仮名／全角平仮名／全角片仮名区別無し
		c=zhirakata(akcnv(toupper(c),BIT1));
	}
	h=gm.htbl[hash(c)];
	if (h!=0) {
		for (i=h;i<gm.max;i++) {
			if (c==gm.gms[i].ch) return(gm.gms[i].str);
		}
	} // 文字がマクロにある時
	return((unchar *)-1);
}

void CloseGrepMACRO()
{
	SetGrepMCSrch(NULL); // マクロ解除
	if (gm.strs!=NULL) free(gm.strs);
	if (gm.gms !=NULL) free(gm.gms);
	gm.strs=NULL;
}

int OpenGrepMACRO(unchar *fname,int mode)
// mode=0:文字変換無し , 1=英大文字小文字、半角片仮名／全角平仮名／全角片仮名区別無し
{
FILE *fp;
int cnt,sz,pt,ln,ret;
#define LMAX	256
unchar line[LMAX],token[LMAX];
unsint cc,h;
#define	SEP	" \t"
	// (-1)重複ﾁｪｯｸ
	if (gm.strs!=NULL) return(100); // 前の設定が残っている
	
	// (0)ファイルオープン
	fp=fopen(fname,"rt");
	if (fp==NULL) return(1);
	
	// (1)初期化
	gm.gms=NULL;	// ワークエリアアドレス
	gm.strs=NULL;	// 文字列ワークエリア
	gm.max=0;	// マクロ数
	// 文字コードに0はない(EOSになるから)
	for (cnt=0;cnt<HSIZE;cnt++) gm.htbl[cnt]=0; // ハッシュ表
	gm.mode=mode;	// 文字判定モード
	
	// (2)行数を数える
	cnt=0;
	while (!read1line2(fp,line,LMAX,1)) cnt++;
	if (cnt==0) return(2); // 内容がない
	gm.max=cnt;
	//printf("cnt=%d\n",cnt);
	
	// (3)ワークエリア確保
	if ((gm.gms=(GrepMACROs *)malloc(sizeof(GrepMACROs)*cnt))==NULL) {
		fclose(fp);
		return(3); // ワークエリアが確保出来ない
	}
	sz=cnt*8; // １文字に付き平均8バイトとする
	if ((gm.strs=malloc(sz))==NULL) {
		CloseGrepMACRO();
		fclose(fp);
		return(4); // ワークエリアが確保出来ない
	}
	//printf("gms=%p,strs=%p\n",gm.gms,gm.strs);
	
	// (3)読み込み＆設定
	rewind(fp);
	cnt=pt=ret=0;
	while (!read1line2(fp,line,LMAX,1)) {
		// 文字設定
		if (!GetToken(line,1,token,SEP)) {
			ret=6; // 文字がない
			break;
		}
		cc=token[0];
		if (check_kanji1(cc)) cc=(cc<<8)|token[1];
		gm.gms[cnt].ch=cc;
		// ハッシュ計算
		h=hash(cc);
		if (gm.htbl[h]==0) gm.htbl[h]=cnt; // 登録
		
		// マクロ文字列設定
		if (!GetToken(line,2,token,SEP)){
			ret=7; // マクロ文字列がない
			break;
		}
		ln=strlen(token)+1; // +1 for EOS
		if (pt+ln>=sz) {
		    unchar *b;
			sz+=256; // 領域を広げる
			if ((b=realloc(gm.strs,sz))==NULL) {
				ret=5; // ワークエリアが確保出来ない
				break;
			}
			gm.strs=b;
		}
		gm.gms[cnt].str=strcpy(&gm.strs[pt],token);
		//printf("token=%s\n",gm.gms[cnt].str);
		pt+=ln;
		cnt++;
	}
	if (ret) CloseGrepMACRO(); // エラー時
	else SetGrepMCSrch(SrchGrepMACRO); // マクロ関数設定
	fclose(fp);
	return(ret);
}
