/* srchword  writen by GOOSE 1991.8.9
** 機能:
**   1ファイルに対し,複数のキーワードで検索し,キーワードを含む行を出力する｡
** 呼び出し形式:
**   srchword  [-all] 検索対象ファイル名
**      -allが指定されていると,必ずファイルの最後まで検索する｡
**      -allがない場合は,未一致のキーワードがなくなった時点で処理を終える｡
** その他:
**   キーワードは標準入力から与える｡従って,ファイルから入力する場合は,
**       srchword  検索対象ファイル名 < キーワードファイル名
**   のようにリダイレクトする｡
**   なお,データ1行に対する文字列比較は,キーワードの1つが一致した時点で打ち
**   切ることに注意｡
**   各キーワードは,次の何れかで区切ること｡
**       ･1つ以上の空白(半角,全角) or
**       ･1つ以上の水平タブ or
**       ･改行 or 終端文字
*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define W0  (ONE_WORD *)0
#define ON  1
#define OFF 0

typedef struct ONE_WORD {                   /* キーワードリストの構造体 */
	struct ONE_WORD *next;
	short           find;                   /* 一致行があったらON */
	short           len;
	char            word[1];                /* GCCなら要素0でもいいのに・・*/
} ONE_WORD;

/********** skipblank **********/
/* 文字列sのi番目以降の最初の空白(全角,半角,水平タブ)以外のオフセットを返す */
short skipblank(short *i, char *s)
{
	for ( ;*(s+*i)!='\n' && *(s+*i)!='\0' ; (*i)++) {
		if (*(s+*i)!=' ' && *(s+*i)!='\t' && memcmp(s+*i,"　",2)!=0)
			break;
		if (memcmp(s+*i,"　",2) == 0) (*i)++;
	}
	if ( *(s+*i) == '\n' ) *(s+*i) = '\0';
	return(*i);
}
/********** skipchar **********/
/* 文字列sのi番目以降の次の文字のオフセットを返す.
**	・最初の空白(1byte,2byte),または
**	・水平タブまたは
**	・最初の指定された文字
**	ALL文字列なら行末('\0')のオフセットを返す.
*/
short skipchar(short *i, char *s, char *c)
{
	for ( ;*(s+*i)!='\n' && *(s+*i)!='\0' ; (*i)++) {
		if (*(s+*i)==' ' || *(s+*i)=='\t' ||
		    *(s+*i)==*c||memcmp(s+*i,"　",2)==0)
			break;
	}
	if ( *(s+*i) == '\n' ) *(s+*i) = '\0';
	return(*i);
}
/********** getoneword **********/
/* 文字列sのi番目以降から1単語を切り出し,ONE_WORD構造体にセットして返す */
ONE_WORD *getoneword(short *keyno,short *i, char *s)
{
	short    top,len;
	ONE_WORD *ans;
	
	top = skipblank(i, s);             /* 先頭の空白を読み飛ばす */
	if (*(s+top) == '\0') return(W0);  /* レコードの終わりなら戻る */
	len = skipchar(i,s,",")-top;
	if ((ans=(ONE_WORD *)malloc(sizeof(ONE_WORD)+len)) == W0) {
		fprintf(stderr,"memory not enough!\n");
		exit(1);
	}
	ans->next = W0;
	ans->find = OFF;
	ans->len = len;
	(*keyno)++;
	memcpy(ans->word, s+top, len);
	*(ans->word+len) = '\0';
	return(ans);
}


/********** main(srchword) **********/
int main(argc, argv)
int  argc;
char *argv[];
{
	short    pos,reccount,len,keyno,all;
	FILE     *fp;
	ONE_WORD *topp,*wp;
	char     indata[256],buf[256],*file;
	
	all = OFF;
	topp = W0;
	keyno = 0;
	if (argc != 2 && argc!=3) {
		fprintf(stderr,"usage: srchword [-all] filename < keyword_filename\n");
		goto eend;
	}
	if (argc == 2)
		file = argv[1];
	else {
		if (strcmp(argv[1],"-all") != 0) {
			fprintf(stderr,"usage: srchword [-all] filename < keyword_filename\n");
			goto eend;
		}
		all = ON;
		file = argv[2];
	}
	
	/* キーワードを読み込んでメモリ上に展開する */
	for (wp=(ONE_WORD *)&topp,pos=0; gets(indata)!=NULL ; pos=0)
		for (; (wp->next=getoneword(&keyno,&pos,indata))!=W0; wp=wp->next);
	
	/* 検索対象ファイルをオープン */
	if ((fp=fopen(file,"r")) == NULL) {
		fprintf(stderr, "file open error!\n");
		goto eend;
	}
	
	/* 検索対象ファイルのレコードがある間繰り返し */
	for (reccount=1,pos=0; fgets(indata,256,fp)!=NULL; reccount++,pos=0) {
		for (skipblank(&pos,indata); indata[pos]!='\0'; pos++) {
			len = strlen(indata+pos);
			/* 切り出した1wordと全キーワードを比較 */
			for (wp=topp; wp!=W0; wp=wp->next) {
				if (len>=wp->len && memcmp(indata+pos,wp->word,wp->len)==0) {
						printf("%d: %s",reccount,indata);
						if (!all && wp->find == OFF) {
							keyno--;
							wp->find = ON;
							if (keyno == 0) goto srchend;
						}
						goto nextrec;
				}
			}
		}
nextrec:;
	}
	
	/* 後処理 */
srchend:;
	fclose(fp);
	for (wp=topp; wp!=W0; wp=topp) {
		topp = wp->next;
		free(wp);
	}
	exit(0);
	
eend:
	exit(1);
}
