/*
 * bookinfo - 電子ブック/EPWING 書籍管理情報表示
 *
 *	Written by Junn Ohta (ohta@src.ricoh.co.jp). Public Domain.
 */

char	*progname = "bookinfo";
char	*version = "1.0";
char	*date = "1999/01/12";
char	*author = "Junn Ohta (ohta@src.ricoh.co.jp)";

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

#define	MEGA		1048576L	/* 1024 * 1024 */
#define	NAME_LEN	28		/* 識別子名称表示幅 */

int	verbose = FALSE;
long	blkno;
uchr	*bookfile;

int	main();
void	usage();
int	bookinfo();
long	estimate();
int	nonzero();
ulng	val();
uchr	*hexstr();
uchr	*jisstr();
uchr	*idname();
int	idmust();
int	markdel();
uchr	*bktype();
uchr	*protinfo();
uchr	*idxhndl();
uchr	*dspvalid();
uchr	*dsplist();
uchr	*dspstyle();
uchr	*idxinfo();

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

    ac--, av++;
    while (ac > 0 && **av == '-') {
	switch (av[0][1]) {
	case 'v':
	case 'V':
	    verbose = TRUE;
	    break;
	case 'd':
	case 'D':
	    if (markdel(&av[0][2]) == ERR)
		usage();
	    break;
	default:
	    usage();
	}
	ac--, av++;
    }
    if (ac < 1 || ac > 2)
	usage();
    bookfile = *av;
    ac--, av++;
    blkno = 1L;
    if (ac == 1)
	blkno = strtol(*av, NULL, 0);

    ret = bookinfo(bookfile, blkno);
    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 [-v] [-d<削除IDリスト>]", progname);
    fprintf(stderr, " <書籍ファイル> [<情報ブロック番号>]\n\n");
    fprintf(stderr, "オプション:\n");
    fprintf(stderr, "    -v: 複合検索管理情報を展開して表示する\n");
    fprintf(stderr, "    -d: 指定IDをサイズ計算でカウントしない\n");
    exit(1);
}

int
bookinfo(bookfile, blkno)
char	*bookfile;
long	blkno;
{
    int		i, j, k, must, mark;
    long	size1, size2, cblks;
    uchr	tmp[32], tmp2[32], tmp3[32], tmp4[32];
    INFO_T	*infop;
    ITEM_T	*itemp;
    CINFO_T	*cinfop;
    CENT_T	*centp;
    CITEM_T	*citemp;
    struct stat	st;

    if (open_book(bookfile) == ERR) {
	fprintf(stderr, "%s: %s がオープンできません\n", progname, bookfile);
	return ERR;
    }
    if ((infop = getinfo(blkno)) == NULL) {
	fprintf(stderr, "%s: 書籍管理情報が取得できません\n", progname);
	return ERR;
    }
    close_book();

    printf("; 電子ブック/EPWING 書籍管理情報");
    printf(" (generated by %s v%s)\n\n", progname, version);
    printf("対象書籍ファイル = %s\n", bookfile);
    printf("情報所在ブロック = %d\n", blkno);
    printf("書籍構成要素数 = %d\n", infop->items);
    printf("予備領域1(書籍種別/保護情報) = %sH\n",
	hexstr(infop->resv1, tmp, RESV1_LEN));
    printf("  書籍種別: %s\n", bktype(infop->resv1));
    printf("  保護情報: %s\n", protinfo(infop->resv1));
    printf("INDEX情報の取扱法 = %02XH (%s)\n",
	infop->idxhndl, idxhndl(infop->idxhndl));
    if (nonzero(infop->resv2, RESV2_LEN))
	printf("予備領域2 = %sH\n", hexstr(infop->resv2, tmp, RESV2_LEN));
    printf("\n");

    printf(" ID 名称                              ");
    printf("   先頭 サイズ INDEX情報 予備3/4\n");
    itemp = infop->item;
    for (i = 0; i < infop->items; i++) {
	must = idmust(itemp->itemid);
	mark = must? '*': ' ';
	if (itemp->itemid != ID_CINFO) {
	    /*
	     * 複合検索以外
	     */
	    printf("%c%02X %-*s      %7lu%7lu %-9s %s/%s\n",
		mark, itemp->itemid, NAME_LEN, idname(itemp->itemid),
		itemp->topblk, itemp->blks, idxinfo(itemp),
		hexstr(itemp->resv3, tmp, RESV3_LEN),
		hexstr(itemp->resv4, tmp2, RESV4_LEN));
	    itemp++;
	    continue;
	}
	/*
	 * 複合検索
	 */
	cblks = itemp->blks;
	cinfop = itemp->cinfo;
	if (verbose) {
	    /*
	     * 複合検索管理情報を詳細表示
	     */
	    printf("%c%02X 複合 %-*s %7lu%7lu %-9s %s/%s\n",
		mark, ID_CINFO, CRESV1_LEN * 2,
		nonzero(cinfop->cresv1, CRESV1_LEN)?
		    hexstr(cinfop->cresv1, tmp, CRESV1_LEN): (uchr *)"",
		itemp->topblk, itemp->blks, idxinfo(itemp),
		hexstr(itemp->resv3, tmp2, RESV3_LEN),
		hexstr(itemp->resv4, tmp3, RESV4_LEN));
	    centp = cinfop->cent;
	    for (j = 0; j < cinfop->cents; j++) {
		printf("  %3s%-*s                            %s\n",
		    (j < cinfop->cents - 1)? "|--": "'--",
		    CNAME_LEN, jisstr(centp->cname, tmp, CNAME_LEN),
		    hexstr(centp->cresv2, tmp2, CRESV2_LEN));
		citemp = centp->citem;
		for (k = 0; k < centp->citems; k++) {
		    printf("  %3s%c%02X %-*s %7lu%7lu           %s/%s\n",
			(j < cinfop->cents - 1)? "|  ": "   ",
			mark, citemp->citemid,
			NAME_LEN, idname(citemp->citemid),
			citemp->ctopblk, citemp->cblks,
			hexstr(citemp->cresv3, tmp, CRESV3_LEN),
			hexstr(citemp->cresv4, tmp2, CRESV4_LEN));
		    cblks += citemp->cblks;
		    citemp++;
		}
		centp++;
	    }
	} else {
	    /*
	     * 複合検索管理情報は合計サイズだけ表示
	     */
	    centp = cinfop->cent;
	    for (j = 0; j < cinfop->cents; j++) {
		citemp = centp->citem;
		for (k = 0; k < centp->citems; k++) {
		    cblks += citemp->cblks;
		    citemp++;
		}
		centp++;
	    }
	    printf("%c%02X %-*s          ***%7lu %-9s %s/%s\n",
		mark, ID_CINFO, NAME_LEN, "複合検索",
		cblks, idxinfo(itemp),
		hexstr(itemp->resv3, tmp, RESV3_LEN),
		hexstr(itemp->resv4, tmp2, RESV4_LEN));
	}
	itemp++;
    }
    printf("\n");

    printf("表示方法の初期値情報:\n");
    printf("  有効無効フラグ = %02XH (%s)\n",
	infop->dspvalid, dspvalid(infop->dspvalid));
    printf("  一覧表表示 = %02XH (%s)\n",
	infop->dsplist, dsplist(infop->dsplist));
    printf("  本文表示法 = %02XH (%s)\n",
	infop->dspstyle, dspstyle(infop->dspstyle));
    if (nonzero(infop->resv5, RESV5_LEN))
	printf("  予備領域5 = %sH\n", hexstr(infop->resv5, tmp, RESV5_LEN));
    if (nonzero(infop->resv6, RESV6_LEN))
	printf("  予備領域6 = %sH\n", hexstr(infop->resv6, tmp, RESV6_LEN));
    printf("\n");

    stat(bookfile, &st);
    size1 = st.st_size;
    size2 = estimate(infop, blkno);
    printf("書籍ファイルサイズ = %3ldMB\n", (size1 + MEGA - 1) / MEGA);
    printf("マーク部分のサイズ = %3ldMB\n", (size2 + MEGA - 1) / MEGA);
    printf("\n");
    freeinfo(infop);
    return OK;
}

/*
 * サイズ計算用のテーブル
 */
typedef	struct slot_t {
    dword	topblk;
    dword	blks;
} SLOT_T;

long
estimate(infop, blkno)
INFO_T	*infop;
long	blkno;
{
    int		i, j, k, m, n, num, must;
    long	blks;
    ITEM_T	*itemp;
    CINFO_T	*cinfop;
    CENT_T	*centp;
    CITEM_T	*citemp;
    SLOT_T	*slot;

    num = 1;
    itemp = infop->item;
    for (i = 0; i < infop->items; i++) {
	must = idmust(itemp->itemid);
	if (!must) {
	    itemp++;
	    continue;
	}
	num++;
	if (itemp->itemid != ID_CINFO) {
	    itemp++;
	    continue;
	}
	cinfop = itemp->cinfo;
	centp = cinfop->cent;
	for (j = 0; j < cinfop->cents; j++) {
	    citemp = centp->citem;
	    for (k = 0; k < centp->citems; k++) {
		num++;
		citemp++;
	    }
	    centp++;
	}
	itemp++;
    }

    slot = (SLOT_T *)malloc(sizeof(SLOT_T) * num);
    if (slot == NULL)
	return 0L;
    slot[0].topblk = blkno;
    slot[0].blks = 1L;
    n = 1;
    itemp = infop->item;
    for (i = 0; i < infop->items; i++) {
	must = idmust(itemp->itemid);
	if (!must) {
	    itemp++;
	    continue;
	}
	for (m = 0; m < n; m++) {
	    if (slot[m].topblk == itemp->topblk)
		break;
	}
	if (m == n) {
	    slot[n].topblk = itemp->topblk;
	    slot[n].blks = itemp->blks;
	    n++;
	}
	if (itemp->itemid != ID_CINFO) {
	    itemp++;
	    continue;
	}
	cinfop = itemp->cinfo;
	centp = cinfop->cent;
	for (j = 0; j < cinfop->cents; j++) {
	    citemp = centp->citem;
	    for (k = 0; k < centp->citems; k++) {
		for (m = 0; m < n; m++) {
		    if (slot[m].topblk == citemp->ctopblk)
			break;
		}
		if (m == n) {
		    slot[n].topblk = citemp->ctopblk;
		    slot[n].blks = citemp->cblks;
		    n++;
		}
		citemp++;
	    }
	    centp++;
	}
	itemp++;
    }

    blks = 0L;
    for (m = 0; m < n; m++)
	blks += slot[m].blks;
    free((char *)slot);
    return blks * BLKSIZ;
}

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

ulng
val(str, len)
uchr	*str;
int	len;
{
    ulng	l;

    l = 0L;
    while (len--)
	l = (l << 8) + *str++;
    return l;
}

uchr *
hexstr(hex, buf, len)
uchr	*hex, *buf;
int	len;
{
    uchr	*p;

    p = buf;
    while (len--) {
	*p++ = "0123456789ABCDEF"[*hex >> 4];
	*p++ = "0123456789ABCDEF"[*hex & 0x0f];
	hex++;
    }
    *p = '\0';
    return buf;
}

uchr *
jisstr(p, buf, len)
uchr	*p, *buf;
int	len;
{
    int		c1, c2;
    uchr	*pend, *q;

    pend = p + len;
    while (pend >= p + 2 &&
	(pend[-1] == '\0' && pend[-2] == '\0' ||
	 pend[-1] == 0x21 && pend[-2] == 0x21))
	pend -= 2;
    q = buf;
#ifdef EUC
    while (p < pend)
	*q++ = (*p++ | 0x80);
#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;
	*q++ = c1;
	*q++ = c2;
    }
#endif
    *q = '\0';
    return buf;
}

/*
 * 書籍構成要素識別子表
 */
typedef struct itype_t {
    byte	idmin;
    byte	idmax;
    int		must;
    uchr	*name;
} ITYPE_T;

ITYPE_T	itypetbl[] = {
    { 0x00, 0x00, TRUE,  "本文"				},
    { 0x01, 0x01, TRUE,  "メニュー"			},
    { 0x02, 0x02, TRUE,  "著作権表示"			},
    { 0x03, 0x03, FALSE, "条件検索見出し"		},
    { 0x04, 0x04, TRUE,  "前方一致かな見出し"		},
    { 0x05, 0x05, TRUE,  "前方一致表記形見出し"		},
    { 0x06, 0x06, TRUE,  "後方一致かな見出し"		},
    { 0x07, 0x07, TRUE,  "後方一致表記形見出し"		},
    { 0x08, 0x08, TRUE,  "前方一致英字見出し"		},
    { 0x09, 0x09, TRUE,  "後方一致英字見出し"		},
    { 0x0a, 0x0a, FALSE, "クロス検索見出し"		},
    { 0x0b, 0x0b, FALSE, "項目番号見出し"		},
    { 0x0d, 0x0d, FALSE, "項目別条件検索見出し"		},
    { 0x0f, 0x0f, FALSE, "図版見出し"			},
    { 0x20, 0x20, FALSE, "メニュー"			},
    { 0x21, 0x21, FALSE, "著作権表示"			},
    { 0x23, 0x23, FALSE, "著作権表示(1行表示用)"	},
    { 0x24, 0x24, FALSE, "音声見出し"			},
    { 0x30, 0x30, FALSE, "かなインデックス"		},
    { 0x40, 0x40, FALSE, "英字インデックス"		},
    { 0x50, 0x50, FALSE, "漢字インデックス"		},
    { 0x60, 0x60, FALSE, "表記形インデックス"		},
    { 0x70, 0x70, TRUE,  "後方一致かなインデックス"	},
    { 0x71, 0x71, TRUE,  "後方一致表記形インデックス"	},
    { 0x72, 0x72, TRUE,  "後方一致英字インデックス"	},
    { 0x80, 0x80, FALSE, "条件検索インデックス"		},
    { 0x81, 0x81, FALSE, "クロス検索インデックス"	},
    { 0x90, 0x90, TRUE,  "前方一致かなインデックス"	},
    { 0x91, 0x91, TRUE,  "前方一致表記形インデックス"	},
    { 0x92, 0x92, TRUE,  "前方一致英字インデックス"	},
    { 0xa0, 0xa0, FALSE, "項目番号インデックス"		},
    { 0xa1, 0xa1, FALSE, "項目別条件検索インデックス"	},
    { 0xb0, 0xb0, FALSE, "ページ検索インデックス"	},
    { 0xb1, 0xb1, FALSE, "漢字インデックス"		},
    { 0xd0, 0xd0, FALSE, "モノクロ図版"			},
    { 0xd1, 0xd1, FALSE, "拡張モノクロ図版"		},
    { 0xd2, 0xd2, FALSE, "カラー図版"			},
    { 0xd8, 0xd8, FALSE, "PCM音声"			},
    { 0xe0, 0xe0, FALSE, "色見本"			},
    { 0xf0, 0xf0, FALSE, "暗証"				},
    { 0xf1, 0xf1, TRUE,  "外字(16×16ドット)"		},
    { 0xf2, 0xf2, TRUE,  "外字(8×16ドット)"		},
    { 0xf3, 0xf3, TRUE,  "外字(24×24ドット)"		},
    { 0xf4, 0xf4, TRUE,  "外字(12×24ドット)"		},
    { 0xf5, 0xf5, TRUE,  "外字(30×30ドット)"		},
    { 0xf6, 0xf6, TRUE,  "外字(15×30ドット)"		},
    { 0xf7, 0xf7, TRUE,  "外字(48×48ドット)"		},
    { 0xf8, 0xf8, TRUE,  "外字(24×48ドット)"		},
    { 0xff, 0xff, FALSE, "複合検索管理情報"		},
    { 0x00, 0x2f, FALSE, "その他のテキストデータ"	},
    { 0x70, 0x7f, FALSE, "その他の後方一致インデックス"	},
    { 0x80, 0x8f, FALSE, "その他の条件検索インデックス"	},
    { 0x90, 0x9f, FALSE, "その他の前方一致インデックス"	},
    { 0xa0, 0xbf, FALSE, "その他のインデックス"		},
    { 0xd0, 0xdf, FALSE, "その他のマルチメディアデータ"	}
};

uchr *
idname(id)
int	id;
{
    int		i, n;

    n = sizeof(itypetbl) / sizeof(itypetbl[0]);
    for (i = 0; i < n; i++) {
	if (id >= itypetbl[i].idmin && id <= itypetbl[i].idmax)
	    return itypetbl[i].name;
    }
    return "???";
}

int
idmust(id)
int	id;
{
    int		i, n;

    n = sizeof(itypetbl) / sizeof(itypetbl[0]);
    for (i = 0; i < n; i++) {
	if (id >= itypetbl[i].idmin && id <= itypetbl[i].idmax)
	    return itypetbl[i].must;
    }
    return FALSE;
}

int
markdel(str)
uchr	*str;
{
    int		i, n;
    int		dmin, dmax;

    n = sizeof(itypetbl) / sizeof(itypetbl[0]);
    for (i = 0; i < n; i++)
	itypetbl[i].must = TRUE;
    while (*str) {
	dmin = dmax = hex(str);
	if (dmin < 0)
	    return ERR;
	str += 2;
	if (*str == '-') {
	    str++;
	    dmax = hex(str);
	    if (dmax < 0)
		return ERR;
	    str += 2;
	}
	for (i = 0; i < n; i++) {
	    if (dmax >= itypetbl[i].idmin && dmin <= itypetbl[i].idmax)
		itypetbl[i].must = FALSE;
	}
	if (*str == ',')
	    str++;
    }
    return OK;
}

uchr *
bktype(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 "類語辞典"; 
    default:   return "不明";
    }
}

uchr *
protinfo(str)
uchr	*str;
{
    static uchr	buf[160];

    strcpy(buf, "表示");
    strcat(buf, (str[1] & 0x01)? "禁止": "許可");
    strcat(buf, ", 印刷");
    strcat(buf, (str[1] & 0x02)? "禁止": "許可");
    strcat(buf, ", テキスト引用");
    strcat(buf, (str[1] & 0x04)? "禁止": "許可");
    strcat(buf, ", 図版引用");
    strcat(buf, (str[1] & 0x08)? "禁止": "許可");
    strcat(buf, ",\n            カラー図版引用");
    strcat(buf, (str[1] & 0x10)? "許可" :"禁止");
    strcat(buf, ", 動画引用");
    strcat(buf, (str[1] & 0x20)? "許可" :"禁止");
    strcat(buf, ", 大量引用");
    strcat(buf, (str[1] & 0x80)? "許可" :"禁止");
    return buf;
}

int
hex(s)
uchr	*s;
{
    int		n;

    if (!isxdigit(s[0]) || !isxdigit(s[1]))
	return -1;
    if (s[0] >= '0' && s[0] <= '9')
	n = s[0] - '0';
    else if (s[0] >= 'A' && s[0] <= 'F')
	n = s[0] - 'A' + 10;
    else if (s[0] >= 'a' && s[0] <= 'f')
	n = s[0] - 'a' + 10;
    n <<= 4;
    if (s[1] >= '0' && s[1] <= '9')
	n += s[1] - '0';
    else if (s[1] >= 'A' && s[1] <= 'F')
	n += s[1] - 'A' + 10;
    else if (s[1] >= 'a' && s[1] <= 'f')
	n += s[1] - 'a' + 10;
    return n;
}

uchr *
idxhndl(n)
int	n;
{
    switch (n) {
    case 0x00: return "INDEX情報有効性に依存";
    case 0x01: return "INDEX情報は無効";
    case 0x02: return "INDEX情報は有効";
    }
    return "不明";
}

uchr *
dspvalid(n)
int	n;
{
    switch (n) {
    case 0x00: return "初期値情報は無効";
    case 0x01: return "初期値情報は有効";
    }
    return "不明";
}

uchr *
dsplist(n)
int	n;
{
    switch (n) {
    case 0x00: return "直接本文表示";
    case 0x01: return "一覧表を表示し、選択指示を受けて表示";
    }
    return "不明";
}

uchr *
dspstyle(n)
int	n;
{
    switch (n) {
    case 0x00: return "検索結果を項目単位に連続して表示";
    case 0x01: return "検索結果を先頭に本文を表示";
    }
    return "不明";
}

/*
 * インデックス構成成分のデータ表現法
 */
#define	I_KATA_MASK	0x00c00000L	/* カタカナ			*/
#define	 I_KATA_HIRA	0x00000000L	/*   ひらがなに変換して記録(k)	*/
#define	 I_KATA_ASIS	0x00400000L	/*   そのまま記録(.)		*/
#define	I_LCASE_MASK	0x00300000L	/* 英小文字			*/
#define	 I_LCASE_UCASE	0x00000000L	/*   英大文字に変換して記録(a)	*/
#define	 I_LCASE_ASIS	0x00100000L	/*   そのまま記録(.)		*/
#define	I_SYM_MASK	0x000c0000L	/* 記号				*/
#define	 I_SYM_DEL	0x00000000L	/*   記号を削除して記録(s)	*/
#define	 I_SIM_ASIS	0x00040000L	/*   そのまま記録(.)		*/
#define	I_CHOU_MASK	0x00030000L	/* 長音				*/
#define	 I_CHOU_BOIN	0x00000000L	/*   直前の母音に変換して記録(c)*/
#define	 I_CHOU_ASIS	0x00010000L	/*   そのまま記録(.)		*/
#define	 I_CHOU_DEL	0x00020000L	/*   長音を削除して記録(C)	*/
#define	I_SOKU_MASK	0x0000c000L	/* 促音				*/
#define	 I_SOKU_LARGE	0x00000000L	/*   大きな文字に変換して記録(t)*/
#define	 I_SOKU_ASIS	0x00004000L	/*   そのまま記録(.)		*/
#define	I_YOU_MASK	0x00003000L	/* 拗音				*/
#define	 I_YOU_LARGE	0x00000000L	/*   大きな文字に変換して記録(y)*/
#define	 I_YOU_ASIS	0x00001000L	/*   そのまま記録(.)		*/
#define	I_GAI_MASK	0x00000c00L	/* 外来語用小文字		*/
#define	 I_GAI_LARGE	0x00000000L	/*   大きな文字に変換して記録(g)*/
#define	 I_GAI_ASIS	0x00000400L	/*   そのまま記録(.)		*/
#define	I_DAKU_MASK	0x00000300L	/* 濁音				*/
#define	 I_DAKU_SEI	0x00000000L	/*   清音に変換して記録(d)	*/
#define	 I_DAKU_ASIS	0x00000100L	/*   そのまま記録(.)		*/
#define	I_HAN_MASK	0x000000c0L	/* 半濁音			*/
#define	 I_HAN_SEI	0x00000000L	/*   清音に変換して記録(h)	*/
#define	 I_HAN_ASIS	0x00000040L	/*   そのまま記録(.)		*/
#define	I_RESV_MASK	0x0000003fL	/* 予備領域			*/

uchr *
idxinfo(item)
ITEM_T	*item;
{
    dword	d;
    static uchr	buf[10];

    if (item->idxvalid == 0x01)
	return "";
    d = item->idxinfo;
    buf[0] = ((d & I_KATA_MASK ) == I_KATA_HIRA  )? 'k': '.';
    buf[1] = ((d & I_LCASE_MASK) == I_LCASE_UCASE)? 'a': '.';
    buf[2] = ((d & I_SYM_MASK  ) == I_SYM_DEL    )? 's': '.';
    buf[3] = ((d & I_CHOU_MASK ) == I_CHOU_BOIN  )? 'c':
	     ((d & I_CHOU_MASK ) == I_CHOU_DEL   )? 'C': '.';
    buf[4] = ((d & I_SOKU_MASK ) == I_SOKU_LARGE )? 't': '.';
    buf[5] = ((d & I_YOU_MASK  ) == I_YOU_LARGE  )? 'y': '.';
    buf[6] = ((d & I_GAI_MASK  ) == I_GAI_LARGE  )? 'g': '.';
    buf[7] = ((d & I_DAKU_MASK ) == I_DAKU_SEI   )? 'd': '.';
    buf[8] = ((d & I_HAN_MASK  ) == I_HAN_SEI    )? 'h': '.';
    buf[9] = '\0';
    return buf;
}
