/*

MercuryInstaller IDXファイル読み込みルーチン

Copyright (c) Delmonta

*/

#include<stdio.h>
#include<stdarg.h>
#include<string.h>
#include<stdlib.h>
#include<dos.h>
#include<farstr.h>
#include<ctype.h>

#include"mercury.h"
/*---------------------------------定数と型----------------------------------*/
typedef	enum
{
	DRIVE,KEYWORD,PROGRAM,MAKE,DIR,MANUAL,README,COPY,XCOPY,COMMANDNUM,
	NULLLINE=COMMANDNUM,
} COMMAND_T;
/*-----------------------------グローバル変数--------------------------------*/
static	char	*Filename=NULL;		/* 読み込み中のファイル名     */
static	int	Linepos;		/* 現在の行番号               */

static	char	*Command[COMMANDNUM] =	/* .idxファイルの各パラメータ */
{
	"DRIVE","KEYWORD","PROGRAM","MAKE","DIR","MANUAL","README",
	"COPY","XCOPY"
};
/*---------------------------エラーを出力して終了----------------------------*/
/* メニュー画面ができあがる前の段階だから、単にメッセージを出力して終了するだ*/
/* けでよい                                                                  */
/*---------------------------------------------------------------------------*/
static	void	die(char *s,...)
{
	va_list	ap;
	va_start(ap,s);

	if	(Filename!=NULL)
		fprintf(stderr,"%s %d:",Filename,Linepos);

	vfprintf(stderr,s,ap);
	putc('\n',stderr);
	exit(1);
}
/*---------------------------ファイルのオープン------------------------------*/
static	FILE	*xfileopen(char *filename)
{
	FILE	*fp=fopen(filename,"r");

	if	(fp==NULL)
		die("%sが見つかりません",filename);

	Filename = filename;

	printf("%sを読み込み中です ",filename);
	return fp;
}
/*-----------------------エラー処理を含むメモリ管理--------------------------*/
static	char	ERR_NOMEM[] = "メモリ不足です";

static void far	*farmalloc_die(size_t size)
{
	void far *p = far_sbrk(size);

	if	(p==NULL)
		die(ERR_NOMEM);

	return p;
}

static	void far *farcalloc_die(size_t size)
{
	void far *p = farmalloc_die(size);

	far_memset(p,'\0',size);

	return p;
}

static	void	*nearmalloc_die(size_t size)
{
	void	*p = malloc(size);
	if	(p==NULL)
		die(ERR_NOMEM);

	return p;
}

static	char	*nearstrdup_die(char *s)
{
	s = strdup(s);

	if	(s==NULL)
		die("dead from nearstrdup");

	return s;
}

static char far	*farstrdup_die(char *s)		/* sはfarポインタではない */
{
	size_t		a = strlen(s)+1;
	char far	*p = farmalloc_die(a);

	far_memcpy(p,s,a);

	return p;
}
/*---------------------------Dataの未定義情報の整理--------------------------*/
static	void	data_storeall(void)
{
	Datanum++;

	if	((Datanum & 63)==0)
		putchar('.');

	if	(Data->make==NULL)
		Data->make = "";

	if	(Data->keywords==0)
		die("検索キーワードが指定されていません");

	#if	0
		if	(Data->dir==NULL && Data->copy==NULL)
			die("データのディレクトリを指定してください");

		/* サークル紹介データのように、マニュアルだけのものも*/
		/* あるかもしれないのでこの警告を出すかどうかは保留  */
	#endif
}
/*-----------------------------KEYWORDコマンド-------------------------------*/
static	void	cmd_keyword(char *p)
{
	if	(Datanum<0)
	{
		for	(p=strtok(p,Separators) ; p!=NULL ;
				p=strtok(NULL,Separators) )
		{
			if	(Keywordnum >= MEMBERSOF(Keyword))
				die("キーワードの数が多すぎます");

			Keyword[Keywordnum++] = nearstrdup_die(p);
		}
	}
	else
	{
		int		i;
		KEYWORD_T	f = 0;

		for	(p=strtok(p,Separators) ; p!=NULL ;
				p=strtok(NULL,Separators) )
		{
			for	(i=0 ; i<Keywordnum ; i++)
			{
				if	(strcmp(Keyword[i],p)==0)
				{
					f |= 1UL<<i;
					goto nexttoken;
				}
			}
			die("不正なキーワードです:%s",p);
		nexttoken:
			;
		}
		Data->keywords = f;
	}
}
/*------------------------------PROGRAMコマンド------------------------------*/
static	void	cmd_program(char *s)
{
	struct DATA far	*data = farcalloc_die(sizeof(struct DATA));

	if	(Datanum>=0)	/* 最初のPROGRAM指定ではない */
	{
		data_storeall();
		data->next = Data;
	}
	else
	{
		data->next = NULL;
		Datanum = 0;
	}

	Data = data;
	Data->title = farstrdup_die(strchop(s,TITLEWIDTH));
}
/*----------------------------COPY/XCOPYコマンド-----------------------------*/
static	void	cmd_copy(char *s,bool isxcopy)
{
	struct	COPYDATA_T far	*p = farmalloc_die(sizeof(struct COPYDATA_T));

	p->string = farstrdup_die(s);
	p->isxcopy = isxcopy;

	p->next = Data->copy;
	Data->copy = p;
}
/*----------------------------トークンの切り出し-----------------------------*/
static	COMMAND_T	getcommand(char *buf,char **next)
{
	int	i;
	char	*p,*q;

	while	(isspace(*buf))
		buf++;

	if	(*buf=='\0')
		return NULLLINE;

	if	((p=strchr(buf,':'))==NULL)
		die("コマンドが不正です:%s",buf);

	q = p+1;
	while	(isspace(*q))
		q++;

	*next = q;

	do
		p--;
	while (isspace(*p));

	*++p = '\0';

	p = strchr(q,'\n');
	if	(p)
		*p = '\0';

	for	(i=0 ; i<MEMBERSOF(Command) ; i++)
	{
		if	(strcmp(buf,Command[i])==0)
			goto end;
	}

	if	(Flag_noignore)
		die("不正なコマンドです:%s\n",buf);
	else
		return NULLLINE;
end:
	return (COMMAND_T)i;
}
/*-----------------------------メインルーチン--------------------------------*/
extern	void	readidxfile(char *filename)
{
	FILE		*fp = xfileopen(filename);
	COMMAND_T	code;
	int		flag = 0; /* コマンドの二重指定を防ぐためのフラグ */
	char		buf[512];
	char		*s;

	Datanum = -1;
	Linepos = 0;

	if	(Drive=='\0')
	{
		if	(filename[1]==':')
			Drive = filename[0];
		else
			Drive = bdos(0x19,0,0) + 'A';	/* カレントドライブ */
	}

	while	(fgets(buf,sizeof(buf),fp)!=NULL)
	{
		Linepos++;

		code = getcommand(buf,&s);
		if	(code==NULLLINE)
			continue;

		if	(Datanum<0 && code>PROGRAM)
			die("PROGRAMコマンドの前に%sコマンドがあります\n",
							Command[code]);

		if	(flag & (1<<code))
			die("%sコマンドが2回指定されています",Command[code]);

		switch	(code)
		{
		case KEYWORD:
			cmd_keyword(s);
			break;

		case PROGRAM:
			cmd_program(s);
			flag = 0;
			break;

		case MAKE:
			flag |= 1<<MAKE;
			Data->make = farstrdup_die(strchop(s,MAKEWIDTH));
			break;

		case DIR:
			flag |= 1<<DIR;
			Data->dir = farstrdup_die(s);
			break;

		case COPY:
		case XCOPY:
			cmd_copy(s,code);
			break;

		case README:
			flag |= 1<<README;
			Data->readme = farstrdup_die(s);
			break;

		case MANUAL:
			flag |= 1<<MANUAL;
			Data->manual = farstrdup_die(s);
			break;

		case DRIVE:
			if	(isalpha(*s))
				Drive=*s;
			else
				die("不正なドライブ名です:%s\n",s);
			break;
		}
	}

	fclose(fp);
	data_storeall();

	putchar('.');
	putchar('\n');
}
/*----------------------------End of idxfile.c-------------------------------*/
