/*

MercuryFilter Version 1.00a for All MS-DOS Machines
Copyright (c) 1995 Delmonta all rights reserved.

コンパイルについて:
	浮動小数点演算は使用していませんので、printf関数は
	doubleに対応していないサブセット版を使用してもかまいません。
*/

#include<ctype.h>
#include<jstring.h>
#include<stdarg.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

/*****************************************************************************/
/*                              下請けルーチン                               */
/*****************************************************************************/
#define	MEMBERSOF(array)	(sizeof(array)/sizeof((array)[0]))

static	char	Separators[] = " \t\n\v";
static	int	Linepos = 0;
/*--------------------------------エラー終了---------------------------------*/
static	void	die(char *s,...)
{
	va_list	ap;

	fprintf(stderr,"%d:",Linepos);

	va_start(ap,s);
	vfprintf(stderr,s,ap);
	va_end(ap);

	exit(1);
}
/*--------------------文字列の両端の空白類をカットする-----------------------*/
static	char	*chop(char *s)
{
	while	(isspace(*s))
		s++;

	if	(*s!='\0')
	{
		char	*p = strchr(s,'\0');

		while	(isspace(*--p))
			;

		*(p+1) = '\0';
	}

	return s;
}
/*-------------------エラー処理を含む文字列の二重化--------------------------*/
static	char	*xstrdup(char *s)
{
	s = strdup(s);
	if	(s!=NULL)
		return s;

	die("メモリ不足です\n");
	exit(1);
}
/*****************************************************************************/
/*                             各キーワードの処理                            */
/*---------------------------------------------------------------------------*/
/* 各キーワードを処理する関数が0を返した場合、その行は出力されない           */
/*****************************************************************************/
static	int	Keywordnum = 0;
static	int	Groupkey_num = 0;

static	char	*Keyword[32];

static	struct
{
	char	*name;
	int	keywordid;
} Groupkey[256];
/*------------キーワードテーブルから指定されたキーワードを探す---------------*/
/* 見つからなければエラー終了する。                                          */
/*---------------------------------------------------------------------------*/
static	int	find_keyword(char *p)
{
	int	i;

	for	(i=0 ; i<Keywordnum ; i++)
	{
		if	(stricmp(p,Keyword[i])==0)
			return i;
	}

	die("キーワード %s は定義されていません\n",p);
	exit(1);
}
/*----------------グループキーテーブルからグループキーを探す-----------------*/
/* 見つからなければ黙って-1を返す。                                          */
/* 返すのは、配列Groupkeyの添え字ではなく、対応するKEYWORDの番号。           */
/*---------------------------------------------------------------------------*/
static	int	find_groupkey(char *p)
{
	int	i;

	for	(i=0 ; i<Groupkey_num ; i++)
	{
		if	(stricmp(p,Groupkey[i].name)==0)
			return Groupkey[i].keywordid;
	}

	return -1;
}
/*--------------------------KEYWORD(1);PROGRAM以前---------------------------*/
static	int	cmd_keyword_def(char *dst,char *src)
{
	char	*p;


	*dst = '\0';

	for	(p=jstrtok(src,Separators);p!=NULL;p=jstrtok(NULL,Separators))
	{
		Keyword[Keywordnum++] = xstrdup(p);
		strcat(dst,p);
		strcat(dst," ");
	}

	return 1;
}
/*-------------------------KEYWORD(2);PROGRAM以後----------------------------*/
static	int	cmd_keyword_ref(char *dst,char *src)
{
	char	*p;

	*dst = '\0';

	for	(p=jstrtok(src,Separators);p!=NULL;p=jstrtok(NULL,Separators))
	{
		int	n;

		n = find_groupkey(p);
		if	(n>=0)
			p = Keyword[n];
		else
			find_keyword(p);

		strcat(dst,p);
		strcat(dst," ");
	}

	return 1;
}
/*---------------------------------GROUP-------------------------------------*/
static	int	cmd_group(char *dst,char *src)
{
	char	*p;
	int	id;

	p = jstrtok(src,Separators);
	if	(p==NULL)
		return 0;

	id = find_keyword(p);
	while	((p=jstrtok(NULL,Separators))!=NULL)
	{
		if	(Groupkey_num==MEMBERSOF(Groupkey))
			die("グループキーの数が多すぎます\n");

		if	(find_groupkey(p)>=0)
			die("グループキー%sが二重に定義されています\n",p);

		Groupkey[Groupkey_num].name      = xstrdup(p);
		Groupkey[Groupkey_num].keywordid = id;

		Groupkey_num++;
	}
	return 0;
}
/*****************************************************************************/
/*                             メインプログラム                              */
/*****************************************************************************/
#define	KEYWORD_DEF	0
#define	KEYWORD_REF	1
#define	GROUP		2

#define	THROUGH		3
#define	CUT		4

static	int	(*Command[3])(char *dst,char *src) =
	{cmd_keyword_def,cmd_keyword_ref,cmd_group};
/*-----------------------------コマンド名の解析------------------------------*/
static	int	getcommandid(char *s)
{
	static	int	isafterprogram = 0;

	static	int	cutnum = 0;		/* 削除するキーワードの数   */
	static	int	throughnum = 6;		/* 素通しするキーワードの数 */

	static	char	*cuttable[256];		/* 削除するキーワード       */
	static	char	*throughtable[256] =	/* 素通しするキーワード     */
		{"DRIVE","MAKE","DIR","README","MANUAL","COPY"};

	int	i;

	strupr(s);

	if	(stricmp(s,"KEYWORD")==0)
	{
		if	(isafterprogram)
			return KEYWORD_REF;
		else
			return KEYWORD_DEF;
	}
	else if	(stricmp(s,"GROUP")==0)
		return GROUP;
	else if	(stricmp(s,"PROGRAM")==0)
	{
		isafterprogram = 1;
		return THROUGH;
	}

	for	(i=0 ; i<cutnum ; i++)
	{
		if	(stricmp(s,cuttable[i])==0)
			return CUT;
	}

	for	(i=0 ; i<throughnum ; i++)
	{
		if	(stricmp(s,throughtable[i])==0)
			return THROUGH;
	}

rep:
	fprintf(stderr,	"未定義のコマンドです %s\n"
			"中止<A>,素通し<T>,削除<C>?",s);

	i = getche();
	putc('\n',stderr);

	switch	(i)
	{
	case 'A':
	case 'a':
		exit(1);
	case 'T':
	case 't':
		if	(throughnum == MEMBERSOF(throughtable))
			die("THROUGHキーワードの数が多すぎます\n");
		throughtable[throughnum++] = xstrdup(s);
		return THROUGH;
	case 'C':
	case 'c':
		if	(cutnum==MEMBERSOF(cuttable))
			die("CUTキーワードの数が多すぎます\n");
		cuttable[cutnum++] = xstrdup(s);
		return CUT;
	default:
		goto rep;
	}
}
/*-----------------------------オプションの解析------------------------------*/
static	void	option(char *s)
{
	switch	(s[1])
	{
	case 'H':
	case 'h':	/* ヘルプは、明示的にオプションを指定して出力する */
	case '?':	/* ものだから、出力先はstdoutにする               */
		printf("MCFILTER ファイル名\n\n");
		break;
	default:
		die("不正なオプションです %s\n",s);
	}
}
/*------------------------------メインルーチン-------------------------------*/
extern	int	main(int argc,char **argv)
{
	FILE	*fpi;

	char	buf[2][512];

	#define	fpo	stdout

	while	(++argv,--argc)
	{
		if	(**argv=='-')
		{
			option(*argv);
			continue;
		}

		if	((fpi=fopen(*argv,"r"))==NULL)
		{
			fprintf(stderr,"%sがオープンできません\n",*argv);
			exit(1);
		}

		Linepos = 0;

		while	(fgets(buf[0],sizeof(buf[0]),fpi)!=NULL)
		{
			char	*p,*q;
			int	id;

			Linepos++;

			p = chop(buf[0]);
			if	(*p=='\0' || *p=='#')	/* 空行･ｺﾒﾝﾄ行の削除 */
				continue;

			q = strchr(p,':');
			if	(q==NULL)
				die("不正な行です  %s\n",p);

			*q = '\0';
			p = chop(p);
			q = chop(q+1);

			id = getcommandid(p);
			if	(id==CUT)
				continue;
			else if	(id!=THROUGH)
			{
				if	(!Command[id](buf[1],q))
					continue;

				q = buf[1];
			}

			fprintf(fpo,"%s:%s\n",p,q);
		}

		fclose(fpi);
	}

	return 0;
}
/*----------------------------End of MCFILTER.C------------------------------*/
