/*
 * catdump - 電子ブック/EPWING カタログ/テキスト変換
 *
 *	Written by Junn Ohta (ohta@src.ricoh.co.jp). Public Domain.
 *      Modified by yamagata@nwgpc.kek.jp on 2000/04/13
 */

char	*progname = "catdump";
char	*version = "1.1";
char	*date = "1999/01/13";
char	*author = "Junn Ohta (ohta@src.ricoh.co.jp)";

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

typedef	unsigned char	uchr;

#ifndef O_BINARY
#define	O_BINARY	0
#endif

#define	OK		0
#define	ERR		(-1)

#define	FALSE		0
#define	TRUE		1

/*
 * 処理内容
 */
#define	DUMP		0	/* カタログ → テキスト */
#define	UNDUMP		1	/* テキスト → カタログ */

/*
 * 書籍種別
 */
#define	EB		0	/* 電子ブック */
#define	EPWING		1	/* EPWING1 = 1, EPWING2 = 2, ... */
#define	EPWING2		2	/* EPWING2 */
#define	EPWING4		4	/* EPWING4 */

/*
 * 書籍ブロックサイズ(= カタログファイルサイズ)
 */
#define	BLKSIZ		2048

/*
 * カタログファイルの構造
 * (登録書籍数はEBで最大50、EPWINGで最大12)
 */
#define	C_BOOKSLEN	  2	/* 登録書籍数			*/
#define	C_CDTYPELEN	  2	/* 書籍種別			*/
#define	C_SELECTLEN	  2	/* 書籍選択画面の有無、書籍番号	*/
#define	C_RSVLEN	 10	/* 将来拡張用			*/
				/* (以下登録書籍数だけくり返し)	*/
#define	B_BKTYPELEN	  2	/*   書籍情報			*/
#define	B_TTLLEN_EB	 30	/*   書籍名称(電子ブック)	*/
#define	B_TTLLEN_EPW	 80	/*   書籍名称(EPWING)		*/
#define	B_DIRLEN	  8	/*   書籍ディレクトリー名	*/
#define	B_DPOSLEN_EPW	  4	/*   ディレクトリー位置(EPWING)	*/
#define	B_INFBLEN_EPW	  2	/*   管理情報記録位置(EPWING)	*/
#define	B_APPLEN_EPW	  4	/*   実装定義(EPWING)		*/
#define	B_ZGAILEN_EPW	 32	/*   全角外字ファイル名(EPWING)	*/
#define	B_HGAILEN_EPW	 32	/*   半角外字ファイル名(EPWING)	*/
				/* (EPWING2以降、以下書籍数だけくり返し) */
#define	B2_RSV1LEN	  4	/*   不明			*/
#define	B2_BKFILELEN	  8	/*   本文ファイル名称		*/
#define	B2_PAD1LEN	 16	/*   不明			*/
#define	B2_STFILELEN	  8	/*   ストリームファイル名称	*/
#define	B2_PAD2LEN	 16	/*   不明			*/
#define	B2_RSV2LEN	  4	/*   不明			*/
#define	B2_PAD3LEN	108	/*   不明			*/

/*
 * カタログヘッダー
 */
typedef struct hdr_t {
    uchr	books[C_BOOKSLEN];
    uchr	cdtype[C_CDTYPELEN];
    uchr	select[C_SELECTLEN];
    uchr	reserved[C_RSVLEN];
} HDR_T;

/*
 * カタログ内容(電子ブック)
 */
typedef struct eb_t {
    uchr	booktype[B_BKTYPELEN];
    uchr	title[B_TTLLEN_EB];
    uchr	directory[B_DIRLEN];
} EB_T;

/*
 * カタログ内容(EPWING)
 */
typedef struct epw_t {
    uchr	booktype[B_BKTYPELEN];
    uchr	title[B_TTLLEN_EPW];
    uchr	directory[B_DIRLEN];
    uchr	dirpos[B_DPOSLEN_EPW];
    uchr	infoblock[B_INFBLEN_EPW];
    uchr	appdef[B_APPLEN_EPW];
    uchr	zgaijifile[B_ZGAILEN_EPW];
    uchr	hgaijifile[B_HGAILEN_EPW];
} EPW_T;

/*
 * カタログ内容(EPWING2以降)
 */
typedef	struct epw2_t {
    uchr	reserved1[B2_RSV1LEN];
    uchr	bookfile[B2_BKFILELEN];
    uchr	pad1[B2_PAD1LEN];
    uchr	streamfile[B2_STFILELEN];
    uchr	pad2[B2_PAD2LEN];
    uchr	reserved2[B2_RSV2LEN];
    uchr	pad3[B2_PAD3LEN];
} EPW2_T;

/*
 * テキストファイルのタグ
 */
#define	EB_ID		"EB"
#define	EPW_ID		"EPWING"
#define	CAT_ENTRY	"[Catalog]"
#define	CTAG_FILENAME	"FileName"
#define	CTAG_BOOKS	"Books"
#define	CTAG_CDTYPE	"Type"
#define	CTAG_SELECT	"BookSelect"
#define	CTAG_RESERVED	"Reserved"
#define	BOOK_ENTRY	"[Book]"
#define	BTAG_BOOKTYPE	"BookType"
#define	BTAG_TITLE	"Title"
#define	BTAG_DIRECTORY	"Directory"
#define	BTAG_DIRPOS	"DirPos"
#define	BTAG_INFOBLOCK	"InfoBlock"
#define	BTAG_APPDEF	"AppDef"
#define	BTAG_ZGAIJIFILE	"ZenGaiji"
#define	BTAG_HGAIJIFILE	"HanGaiji"
#define	B2TAG_RSV1	"Reserved1"
#define	B2TAG_BKFILE	"BookFile"
#define	B2TAG_PAD1	"Padding1"
#define	B2TAG_STFILE	"StreamFile"
#define	B2TAG_PAD2	"Padding2"
#define	B2TAG_RSV2	"Reserved2"
#define	B2TAG_PAD3	"Padding3"

/*
 * 処理済みマスク
 */
#define	M_FILENAME	0x00000001L
#define	M_BOOKS		0x00000002L
#define	M_CDTYPE	0x00000004L	/* 必須 */
#define	M_SELECT	0x00000008L
#define	M_RESERVED	0x00000010L
#define	M_BOOKTYPE	0x00000020L	/* 必須 */
#define	M_TITLE		0x00000040L	/* 必須 */
#define	M_DIRECTORY	0x00000080L	/* 必須 */
#define	M_DIRPOS	0x00000100L
#define	M_INFOBLOCK	0x00000200L
#define	M_APPDEF	0x00000400L
#define	M_ZGAIJIFILE	0x00000800L
#define	M_HGAIJIFILE	0x00001000L
#define	M_RESERVED1	0x00002000L
#define	M_BOOKFILE	0x00004000L
#define	M_PADDING1	0x00008000L
#define	M_STREAMFILE	0x00010000L
#define	M_PADDING2	0x00020000L
#define	M_RESERVED2	0x00040000L
#define	M_PADDING3	0x00080000L

/*
 * getstr()の処理方法
 */
#define	F_NUL		0	/* 余りを0x00で埋める */
#define	F_SPACE		1	/* 余りをスペースで埋める */

int	proctype = DUMP;
int	type;
int	line;
char	*catalog;
char	*txtfile;
uchr	catbuf[BLKSIZ * 2];
uchr	epw2buf[BLKSIZ];
uchr	buf[BUFSIZ];

int	EBGmode = 0;

int	main();
void	usage();
int	dump();
uchr	*bookkind();
int	guess();
int	nonzero();
void	outhex();
void	outstr();
void	outjstr();
int	undump();
uchr	*getline();
uchr	*getvalue();
int	gethex();
int	hexdigit();
int	getstr();
int	getjstr();

int
main(ac, av)
int	ac;
char	**av;
{
    int		ret;

    ac--, av++;
    while (ac > 0 && **av == '-') {
	switch (av[0][1]) {
	case 'g':
	case 'G':
	    EBGmode = 1;
	    break;
	case 'd':
	case 'D':
	    proctype = DUMP;
	    break;
	case 'u':
	case 'U':
	    proctype = UNDUMP;
	    ac--, av++;
	    if (ac <= 0)
		usage();
	    txtfile = *av;
	    break;
	default:
	    usage();
	}
	ac--, av++;
    }
    if (ac != 1)
	usage();
    catalog = *av;
    switch (proctype) {
    case DUMP:
	ret = dump(catalog);
	break;
    case UNDUMP:
	ret = undump(txtfile, catalog);
	break;
    }
    if (ret == ERR)
	exit(1);
    exit(0);
}

void
usage()
{
    fprintf(stderr, "電子ブック/EPWING カタログ/テキスト変換");
    fprintf(stderr, " Ver.%s (%s)\n    Written by %s, Public Domain.\n\n",
	version, date, author);
    fprintf(stderr, "使用法: %s", progname);
    fprintf(stderr, " [-g] [-d] [-u <テキストファイル>] <カタログファイル>\n\n");
    fprintf(stderr, "オプション:\n");
    fprintf(stderr, "    -g: EBG 専用モードにする\n");
    fprintf(stderr, "    -d: カタログファイルを標準出力にダンプする\n");
    fprintf(stderr, "    -u: テキストファイルをカタログファイルに変換する\n");
    exit(1);
}

int
dump(catalog)
char	*catalog;
{
    int		fd, i, num;
    HDR_T	*hdr;
    EB_T	*eb;
    EPW_T	*epw;
    EPW2_T	*epw2;
    struct stat	st;

    if (stat(catalog, &st) < 0) {
	fprintf(stderr, "%s の情報が取得できません\n", catalog);
	return ERR;
    }
    if (st.st_size < 0 || st.st_size > BLKSIZ * 2) {
	fprintf(stderr, "カタログサイズが異常です\n");
	return ERR;
    }
    if ((fd = open(catalog, O_RDONLY|O_BINARY)) < 0) {
	fprintf(stderr, "%s がオープンできません\n", catalog);
	return ERR;
    }
    if (read(fd, (char *)catbuf, st.st_size) < st.st_size) {
	fprintf(stderr, "ファイルの読み込みに失敗しました\n");
	close(fd);
	return ERR;
    }
    close(fd);

    hdr = (HDR_T *)catbuf;
    num = (hdr->books[0] << 8) + hdr->books[1];
    type = hdr->cdtype[1];
    printf("; 電子ブック/EPWING カタログ内容");
    printf(" (generated by %s v%s)\n\n", progname, version);
    printf("%s\n", CAT_ENTRY);
    printf("%-11s= %s\n", CTAG_FILENAME, catalog);
    if (type == EB)
	printf("%-11s= %s\n", CTAG_CDTYPE, EB_ID);
    else
	printf("%-11s= %s%d\n", CTAG_CDTYPE, EPW_ID, type);
    printf("%-11s= %d\n", CTAG_BOOKS, num);
    if (hdr->select[0] == 0x01) {
	printf("%-11s= %d\n", CTAG_SELECT, 
	    (hdr->select[1] >> 4) * 10 + (hdr->select[1] & 0x0f));
    }
    if (type >= EPWING4 || nonzero(hdr->reserved, C_RSVLEN)) {
	printf("%-11s= ", CTAG_RESERVED);
	outhex(hdr->reserved, C_RSVLEN);
	if (type >= EPWING4) {
	    printf(" (HD利用:%s, NETWORK利用:%s)",
		(hdr->reserved[2] & 0x10)? "許可": "禁止",
		(hdr->reserved[2] & 0x01)? "許可": "禁止");
	}
	printf("\n");
    }
    printf("\n");
    if (type == EB) {
	eb = (EB_T *)(catbuf + sizeof(HDR_T));
	for (i = 0; i < num; i++) {
	    printf("%s\n", BOOK_ENTRY);
	    printf("%-11s= ", BTAG_BOOKTYPE);
	    outhex(eb->booktype, B_BKTYPELEN);
	    printf("\n");
	    printf("%-11s= \"", BTAG_TITLE);

	    if (!EBGmode) {
		outjstr(eb->title, B_TTLLEN_EB);
	    } else {
		outstr(eb->title, B_TTLLEN_EB);
	    }

	    printf("\"\n");
	    printf("%-11s= \"", BTAG_DIRECTORY);
	    outstr(eb->directory, B_DIRLEN);
	    printf("\"\n");
	    printf("\n");
	    eb++;
	}
    } else {
	epw = (EPW_T *)(catbuf + sizeof(HDR_T));
	epw2 = (EPW2_T *)((uchr *)epw + num * sizeof(EPW_T));
	for (i = 0; i < num; i++) {
	    printf("%s\n", BOOK_ENTRY);
	    printf("%-11s= ", BTAG_BOOKTYPE);
	    outhex(epw->booktype, B_BKTYPELEN);
	    printf(" (%02X:%s, %02X:EPWING%d)\n",
		epw->booktype[0], bookkind(epw->booktype),
		epw->booktype[1], epw->booktype[1]);
	    printf("%-11s= \"", BTAG_TITLE);
	    if (!EBGmode) {
		outjstr(epw->title, B_TTLLEN_EPW);
	    } else {
		outstr(epw->title, B_TTLLEN_EPW);
	    }
	    printf("\"\n");
	    printf("%-11s= \"", BTAG_DIRECTORY);
	    outstr(epw->directory, B_DIRLEN);
	    printf("\"\n");
	    if (nonzero(epw->dirpos, B_DPOSLEN_EPW)) {
		printf("%-11s= ", BTAG_DIRPOS);
		outhex(epw->dirpos, B_DPOSLEN_EPW);
		printf("\n");
	    }
	    if (nonzero(epw->infoblock, B_INFBLEN_EPW)) {
		printf("%-11s= ", BTAG_INFOBLOCK);
		outhex(epw->infoblock, B_INFBLEN_EPW);
		printf("\n");
	    }
	    if (nonzero(epw->appdef, B_APPLEN_EPW)) {
		printf("%-11s= ", BTAG_APPDEF);
		outhex(epw->appdef, B_APPLEN_EPW);
		printf("\n");
	    }
	    if (nonzero(epw->zgaijifile, B_ZGAILEN_EPW)) {
		printf("%-11s= \"", BTAG_ZGAIJIFILE);
		outstr(epw->zgaijifile, B_ZGAILEN_EPW);
		printf("\"\n");
	    }
	    if (nonzero(epw->hgaijifile, B_HGAILEN_EPW)) {
		printf("%-11s= \"", BTAG_HGAIJIFILE);
		outstr(epw->hgaijifile, B_HGAILEN_EPW);
		printf("\"\n");
	    }
	    if (type >= EPWING2) {
		if (nonzero(epw2->bookfile, B2_BKFILELEN)) {
		    printf("%-11s= \"", B2TAG_BKFILE);
		    outstr(epw2->bookfile, B2_BKFILELEN);
		    printf("\"\n");
		}
		if (nonzero(epw2->streamfile, B2_STFILELEN)) {
		    printf("%-11s= \"", B2TAG_STFILE);
		    outstr(epw2->streamfile, B2_STFILELEN);
		    printf("\"\n");
		}
		if (nonzero(epw2->reserved1, B2_RSV1LEN)) {
		    printf("%-11s= ", B2TAG_RSV1);
		    outhex(epw2->reserved1, B2_RSV1LEN);
		    printf("\n");
		}
		if (nonzero(epw2->reserved2, B2_RSV2LEN)) {
		    printf("%-11s= ", B2TAG_RSV2);
		    outhex(epw2->reserved2, B2_RSV2LEN);
		    printf("\n");
		}
		if (nonzero(epw2->pad1, B2_PAD1LEN)) {
		    printf("%-11s= ", B2TAG_PAD1);
		    outhex(epw2->pad1, B2_PAD1LEN);
		    printf("\n");
		}
		if (nonzero(epw2->pad2, B2_PAD2LEN)) {
		    printf("%-11s= ", B2TAG_PAD2);
		    outhex(epw2->pad2, B2_PAD2LEN);
		    printf("\n");
		}
		if (nonzero(epw2->pad3, B2_PAD3LEN)) {
		    printf("%-11s= ", B2TAG_PAD3);
		    outhex(epw2->pad3, B2_PAD3LEN);
		    printf("\n");
		}
	    }
	    printf("\n");
	    epw++;
	    epw2++;
	}
    }
    return OK;
}

uchr *
bookkind(str)
uchr	*str;
{
    switch (*str & 0xf0) {
    case 0x00: return "国語辞典"; 
    case 0x10: return "漢和辞典"; 
    case 0x20: return "英和辞典"; 
    case 0x30: return "和英辞典"; 
    case 0x40: return "現代用語辞典"; 
    case 0x50: return "百科事典"; 
    case 0x60: return "一般書物"; 
    case 0x70: return "類語辞典"; 
    case 0xf0: return "ストリーム";
    default:   return "不明";
    }
}

int
nonzero(p, len)
uchr	*p;
int	len;
{
    while (len--)
	if (*p++)
	    return TRUE;
    return FALSE;
}

void
outhex(p, len)
uchr	*p;
int	len;
{
    while (len--)
	printf("%02X", *p++);
}

void
outstr(p, len)
uchr	*p;
int	len;
{
    uchr	*pend;

    pend = p + len;
    while (pend > p && (pend[-1] == '\0' || pend[-1] == ' '))
	pend--;
    while (p < pend) {
	if (*p >= 0x20 && *p <= 0x7f)
	    putchar(*p);
	else if (*p == '"' || *p == '\\')
	    printf("\\%c", *p);
	else if (*p == '\0')
	    printf("\\0");
	else
	    printf("\\x%02X", *p);
	p++;
    }
}

void
outjstr(p, len)
uchr	*p;
int	len;
{
    int		c1, c2;
    uchr	*pend;

    pend = p + len;
    while (pend >= p + 2 &&
	(pend[-1] == '\0' && pend[-2] == '\0' ||
	 pend[-1] == 0x21 && pend[-2] == 0x21))
	pend -= 2;
#ifdef EUC
    while (p < pend) {
	putchar(*p | 0x80);
	p++;
    }
#endif
#ifdef SJIS
    while (p < pend) {
	c1 = *p++;
	c2 = *p++;
	if (c1 & 0x01) {
	    c2 += 0x1f;
	    if (c2 > 0x7e)
		c2++;
	} else {
	    c2 += 0x7e;
	}
	c1 = (c1 + 0xe1) >> 1;
	if (c1 > 0x9f)
	    c1 += 0x40;
	putchar(c1);
	putchar(c2);
    }
#endif
}

int
undump(txtfile, catalog)
char	*txtfile, *catalog;
{
    int		fd, i, st, num, len, err;
    long	mask;
    uchr	*p, *t, *u;
    FILE	*fp;
    HDR_T	*hdr;
    EB_T	*eb;
    EPW_T	*epw;
    EPW2_T	*epw2;

    if ((fp = fopen(txtfile, "r")) == NULL) {
	fprintf(stderr, "%s がオープンできません\n", txtfile);
	return ERR;
    }
    memset(catbuf, '\0', BLKSIZ * 2);
    memset(epw2buf, '\0', BLKSIZ);
    err = 0;
    line = 0;

    if (getline(buf, fp) == NULL ||
	strncmp(buf, CAT_ENTRY, strlen(CAT_ENTRY))) {
	fprintf(stderr, "ERR: 項目 %s がありません\n", CAT_ENTRY);
	fclose(fp);
	return ERR;
    }
    type = EB;
    st = 0;
    hdr = (HDR_T *)catbuf;
    mask = 0L;
    while (getline(buf, fp) != NULL && *buf != '[') {
	if ((p = getvalue(buf)) == NULL) {
	    fprintf(stderr, "ERR: line %d: 構文に誤りがあります\n", line);
	    err++;
	    continue;
	}
	if (!strcmp(buf, CTAG_FILENAME)) {
	    if ((mask & M_FILENAME) != 0)
		goto ctag_dup;
	    /*
	     * ファイル名の情報は使わないので読み飛ばす
	     */
	    mask |= M_FILENAME;
	} else if (!strcmp(buf, CTAG_BOOKS)) {
	    if ((mask & M_BOOKS) != 0)
		goto ctag_dup;
	    /*
	     * 書籍数の情報は使わないので読み飛ばす
	     */
	    mask |= M_BOOKS;
	} else if (!strcmp(buf, CTAG_CDTYPE)) {
	    len = strlen(EPW_ID);
	    if ((mask & M_BOOKTYPE) != 0)
		goto ctag_dup;
	    if (!strcmp(p, EB_ID)) {
		type = EB;
	    } else if (!strncmp(p, EPW_ID, len) &&
		p[len] >= '1' && p[len] <= '9' && p[len+1] == '\0') {
		type = EPWING + p[len] - '1';
	    } else {
		fprintf(stderr, "ERR: line %d: %s の値に誤りがあります(%s)\n",
		    line, CTAG_CDTYPE, p);
		fclose(fp);
		return ERR;
	    }
	    hdr->cdtype[1] = type;
	    mask |= M_BOOKTYPE;
	} else if (!strcmp(buf, CTAG_SELECT)) {
	    if ((mask & M_SELECT) != 0)
		goto ctag_dup;
	    st = 0;
	    while (isdigit(*p)) {
		st = st * 10 + *p - '0';
		p++;
	    }
	    if (*p == '\0' && i > 0) {
		hdr->select[0] = 0x01;
		hdr->select[1] = ((st / 10) << 4) + (st % 10);
	    } else {
		fprintf(stderr, "ERR: line %d: %s の値に誤りがあります(%s)\n",
		    line, CTAG_SELECT, p);
		fclose(fp);
		return ERR;
	    }
	    mask |= M_SELECT;
	} else if (!strcmp(buf, CTAG_RESERVED)) {
	    if ((mask & M_RESERVED) != 0)
		goto ctag_dup;
	    mask |= M_RESERVED;
	    if (gethex(hdr->reserved, p, C_RSVLEN) == ERR)
		goto ctag_invalid;
	} else {
	    fprintf(stderr, "ERR: line %d: 不明のタグです(%s)\n",
		line, buf);
	    err++;
	    continue;
	}
	continue;
    ctag_dup:
	fprintf(stderr, "ERR: line %d: タグが二重定義されています(%s)\n",
	    line, buf);
	err++;
	continue;
    ctag_invalid:
	fprintf(stderr, "ERR: line %d: 値が不正です(%s)\n", line, p);
	err++;
	continue;
    }
    if ((mask & M_BOOKTYPE) == 0) {
	fprintf(stderr, "ERR: line %d: %s が見つかりませんでした\n",
	    line, CTAG_CDTYPE);
	err++;
    }

    if (type == EB) {
	eb = (EB_T *)(catbuf + sizeof(HDR_T));
    } else {
	epw = (EPW_T *)(catbuf + sizeof(HDR_T));
	epw2 = (EPW2_T *)epw2buf;
    }
    num = 0;
    while (*buf == '[') {
	if (strncmp(buf, BOOK_ENTRY, strlen(BOOK_ENTRY))) {
	    fprintf(stderr, "ERR: line %d: 不明な項目です(%s)\n", line, buf);
	    err++;
	    while (getline(buf, fp) != NULL && *buf != '[')
		;
	    if (*buf == '\0')
		break;
	    continue;
	}
	if (type == EB && (uchr *)&eb[1] >= catbuf + BLKSIZ ||
	    type > EB && (uchr *)&epw[1] >= catbuf + BLKSIZ) {
	    fprintf(stderr, "ERR: line %d: 項目 %s の個数が多すぎます\n",
		line, BOOK_ENTRY);
	    err++;
	    break;
	}
	mask = 0;
	while (getline(buf, fp) != NULL && *buf != '[') {
	    if ((p = getvalue(buf)) == NULL) {
		fprintf(stderr, "ERR: line %d: 構文に誤りがあります\n", line);
		err++;
		continue;
	    }
	    if (!strcmp(buf, BTAG_BOOKTYPE)) {
		if ((mask & M_BOOKTYPE) != 0)
		    goto btag_dup;
		mask |= M_BOOKTYPE;
		t = (type == EB)? eb->booktype: epw->booktype;
		if (gethex(t, p, B_BKTYPELEN) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, BTAG_TITLE)) {
		if ((mask & M_TITLE) != 0)
		    goto btag_dup;
		mask |= M_TITLE;
		if (type == EB) {
		    t = eb->title;
		    len = B_TTLLEN_EB;
		} else {
		    t = epw->title;
		    len = B_TTLLEN_EPW;
		}
		if (!EBGmode) {
		    if (getjstr(t, p, len) == ERR)
			goto btag_invalid;
		} else {
		    if (getstr(t, p, len) == ERR)
			goto btag_invalid;
		}
	    } else if (!strcmp(buf, BTAG_DIRECTORY)) {
		if ((mask & M_DIRECTORY) != 0)
		    goto btag_dup;
		mask |= M_DIRECTORY;
		t = (type == EB)? eb->directory: epw->directory;
		if (getstr(t, p, B_DIRLEN, F_SPACE) == ERR)
		    goto btag_invalid;
		u = t;
		for (i = 0; i < num; i++) {
		    u -= (type == EB)? sizeof(EB_T): sizeof(EPW_T);
		    if (!memcmp(t, u, B_DIRLEN)) {
			fprintf(stderr,
			    "ERR: line %d: %s の値が重複しています(%s)\n",
			    line, buf, p);
			err++;
			break;
		    }
		}
	    } else if (!strcmp(buf, BTAG_DIRPOS)) {
		if (type < EPWING)
		    goto btag_epwonly;
		if ((mask & M_DIRPOS) != 0)
		    goto btag_dup;
		mask |= M_DIRPOS;
		if (gethex(epw->dirpos, p, B_DPOSLEN_EPW) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, BTAG_INFOBLOCK)) {
		if (type < EPWING)
		    goto btag_epwonly;
		if ((mask & M_INFOBLOCK) != 0)
		    goto btag_dup;
		mask |= M_INFOBLOCK;
		if (gethex(epw->infoblock, p, B_INFBLEN_EPW) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, BTAG_APPDEF)) {
		if (type < EPWING)
		    goto btag_epwonly;
		if ((mask & M_APPDEF) != 0)
		    goto btag_dup;
		mask |= M_APPDEF;
		if (gethex(epw->appdef, p, B_APPLEN_EPW) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, BTAG_ZGAIJIFILE)) {
		if (type < EPWING)
		    goto btag_epwonly;
		if ((mask & M_ZGAIJIFILE) != 0)
		    goto btag_dup;
		mask |= M_ZGAIJIFILE;
		if (getstr(epw->zgaijifile, p, B_ZGAILEN_EPW, F_NUL) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, BTAG_HGAIJIFILE)) {
		if (type < EPWING)
		    goto btag_epwonly;
		if ((mask & M_HGAIJIFILE) != 0)
		    goto btag_dup;
		mask |= M_HGAIJIFILE;
		if (getstr(epw->hgaijifile, p, B_HGAILEN_EPW, F_NUL) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, B2TAG_RSV1)) {
		if (type < EPWING2)
		    goto btag_epw2only;
		if ((mask & M_RESERVED1) != 0)
		    goto btag_dup;
		mask |= M_RESERVED1;
		if (gethex(epw2->reserved1, p, B2_RSV1LEN) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, B2TAG_BKFILE)) {
		if (type < EPWING2)
		    goto btag_epw2only;
		if ((mask & M_BOOKFILE) != 0)
		    goto btag_dup;
		mask |= M_BOOKFILE;
		if (getstr(epw2->bookfile, p, B2_BKFILELEN, F_SPACE) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, B2TAG_PAD1)) {
		if (type < EPWING2)
		    goto btag_epw2only;
		if ((mask & M_PADDING1) != 0)
		    goto btag_dup;
		mask |= M_PADDING1;
		if (gethex(epw2->pad1, p, B2_PAD1LEN) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, B2TAG_STFILE)) {
		if (type < EPWING2)
		    goto btag_epw2only;
		if ((mask & M_STREAMFILE) != 0)
		    goto btag_dup;
		mask |= M_STREAMFILE;
		if (getstr(epw2->streamfile, p, B2_STFILELEN, F_SPACE) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, B2TAG_PAD2)) {
		if (type < EPWING2)
		    goto btag_epw2only;
		if ((mask & M_PADDING2) != 0)
		    goto btag_dup;
		mask |= M_PADDING2;
		if (gethex(epw2->pad2, p, B2_PAD2LEN) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, B2TAG_RSV2)) {
		if (type < EPWING2)
		    goto btag_epw2only;
		if ((mask & M_RESERVED2) != 0)
		    goto btag_dup;
		mask |= M_RESERVED2;
		if (gethex(epw2->reserved2, p, B2_RSV2LEN) == ERR)
		    goto btag_invalid;
	    } else if (!strcmp(buf, B2TAG_PAD3)) {
		if (type < EPWING2)
		    goto btag_epw2only;
		if ((mask & M_PADDING3) != 0)
		    goto btag_dup;
		mask |= M_PADDING3;
		if (gethex(epw2->pad3, p, B2_PAD3LEN) == ERR)
		    goto btag_invalid;
	    } else {
		fprintf(stderr, "ERR: line %d: 不明のタグです(%s)\n",
		    line, buf);
		err++;
		continue;
	    }
	    continue;
	btag_epwonly:
	    fprintf(stderr, "ERR: line %d: このタグはEPWING専用です(%s)\n",
		line, buf);
	    err++;
	    continue;
	btag_epw2only:
	    fprintf(stderr, "ERR: line %d: このタグはEPWING2以降専用です(%s)\n",
		line, buf);
	    err++;
	    continue;
	btag_dup:
	    fprintf(stderr, "ERR: line %d: タグが二重定義されています(%s)\n",
		line, buf);
	    err++;
	    continue;
	btag_invalid:
	    fprintf(stderr, "ERR: line %d: 値が不正です(%s)\n", line, p);
	    err++;
	    continue;
	}
	if (type >= EPWING && (mask & M_BOOKTYPE) == 0) {
	    fprintf(stderr, "ERR: line %d: %s が見つかりませんでした\n",
		line, BTAG_BOOKTYPE);
	    err++;
	}
	if ((mask & M_TITLE) == 0) {
	    fprintf(stderr, "ERR: line %d: %s が見つかりませんでした\n",
		line, BTAG_TITLE);
	    err++;
	}
	if ((mask & M_DIRECTORY) == 0) {
	    fprintf(stderr, "ERR: line %d: %s が見つかりませんでした\n",
		line, BTAG_DIRECTORY);
	    err++;
	}
	if (type >= EPWING && (mask & M_INFOBLOCK) == 0) {
	    if (epw->booktype[0] != 0xf0) {
		/*
		 * ストリーム書籍以外なら
		 * 書籍管理情報ブロックは
		 * 第1ブロックと仮定する
		 */
		epw->infoblock[1] = 0x01;
	    }
	}
	if (type == EB) {
	    eb++;
	} else {
	    epw++;
	    epw2++;
	}
	num++;
    }
    hdr->books[0] = (num >> 8) & 0xff;
    hdr->books[1] = num & 0xff;
    if (st > num) {
	fprintf(stderr, "ERR: %s の値が書籍数を超えています\n", CTAG_SELECT);
	err++;
    }
    if (type >= EPWING2)
	memcpy((char *)epw, epw2buf, sizeof(EPW2_T) * num);

    fclose(fp);
    if (err)
	return ERR;

    if ((fd = open(catalog, O_WRONLY|O_BINARY|O_CREAT|O_TRUNC, 0644)) < 0) {
	fprintf(stderr, "%s が作成できません\n", catalog);
	return ERR;
    }
    len = BLKSIZ;
    if (type >= EPWING2) {
	if (sizeof(HDR_T) + (sizeof(EPW_T) + sizeof(EPW2_T)) * num > BLKSIZ)
	    len = BLKSIZ * 2;
    }
    if (write(fd, (char *)catbuf, len) != len) {
	fprintf(stderr, "書き込みに失敗しました\n");
	close(fd);
	return ERR;
    }
    close(fd);
    return OK;
}

uchr *
getline(buf, fp)
uchr	*buf;
FILE	*fp;
{
    for (;;) {
	if (fgets(buf, BUFSIZ, fp) == NULL) {
	    *buf = '\0';
	    return NULL;
	}
	line++;
	if (*buf != '\0' && *buf != '\n' && *buf != ';')
	    break;
    }
    buf[strlen(buf) - 1] = '\0';
    return buf;
}

uchr *
getvalue(buf)
uchr	*buf;
{
    uchr	*p, *q;

    p = buf;
    while (*p && *p != '=' && *p != ' ' && *p != '\t')
	p++;
    q = p;
    while (*p == ' ' || *p == '\t')
	p++;
    if (*p != '=')
	return NULL;
    *q = '\0';
    p++;
    while (*p == ' ' || *p == '\t')
	p++;
    q = p;
    if (*q == '"') {
	q++;
	while (*q && *q != '"') {
	    if (*q == '\\' && q[1])
		q++;
	    q++;
	}
	if (*q != '"')
	    return NULL;
	q++;
    } else {
	while (*q && *q != ' ' && *q != '\t')
	    q++;
    }
    *q = '\0';
    return p;
}

int
gethex(buf, str, len)
uchr	*buf, *str;
int	len;
{
    while (len-- > 0) {
	if (!isxdigit(str[0]) || !isxdigit(str[1]))
	    break;
	*buf = hexdigit(*str++) << 4;
	*buf |= hexdigit(*str++);
	buf++;
    }
    if (*str != '\0')
	return ERR;
    while (len-- > 0)
	*buf++ = '\0';
    return OK;
}

int
hexdigit(c)
int	c;
{
    if (c >= '0' && c <= '9')
	return c - '0';
    else if (c >= 'A' && c <= 'F')
	return c - 'A' + 10;
    else if (c >= 'a' && c <= 'f')
	return c - 'a' + 10;
    return -1;
}

int
getstr(buf, str, len, type)
uchr	*buf, *str;
int	len, type;
{
    if (*str++ != '"')
	return ERR;
    while (len > 0) {
	if (*str == '\0')
	    return ERR;
	if (*str == '"')
	    break;
	if (*str != '\\') {
	    *buf++ = *str++;
	    len--;
	    continue;
	}
	str++;
	if (*str == '0') {
	    *buf++ = '\0';
	    str++;
	} else if (*str == 'x' || *str == 'X') {
	    str++;
	    if (gethex(buf, str, 1) == ERR)
		return ERR;
	    buf++;
	    str += 2;
	} else {
	    *buf++ = *str++;
	}
	len--;
    }
    if (*str != '"')
	return ERR;
    if (type == F_NUL) {
	while (len-- > 0)
	    *buf++ = '\0';
    } else {
	while (len-- > 0)
	    *buf++ = ' ';
    }
    return OK;
}

int
getjstr(buf, str, len)
uchr	*buf, *str;
int	len;
{
    int		c1, c2;

    if (*str++ != '"')
	return ERR;
#ifdef EUC
    while (len > 0) {
	if (*str == '\0')
	    return ERR;
	if (*str == '"')
	    break;
	if (*str < 0xa1 || *str > 0xfe || str[1] < 0xa1 || str[1] > 0xfe)
	    return ERR;
	*buf++ = *str++ & 0x7f;
	*buf++ = *str++ & 0x7f;
	len -= 2;
    }
#endif
#ifdef SJIS
    while (len > 0) {
	if (*str == '\0')
	    return ERR;
	if (*str == '"')
	    break;
	c1 = *str++;
	c2 = *str++;
	if (c1 < 0x81 || c1 > 0x9f && c1 < 0xe0 || c1 > 0xef)
	    return ERR;
	if (c1 > 0x9f)
	    c1 -= 0x40;
	c1 += c1;
	if (c2 <= 0x9e) {
	    c1 -= 0xe1;
	    if (c2 >= 0x80)
		c2 -= 1;
	    c2 -= 0x1f;
	} else {
	    c1 -= 0xe0;
	    c2 -= 0x7e;
	}
	*buf++ = c1;
	*buf++ = c2;
	len -= 2;
    }
#endif
    if (*str != '"')
	return ERR;
    while (len-- > 0)
	*buf++ = '\0';
    return OK;
}
