/*
 * squeeze - 電子ブック/EPWING 書籍軽量化
 *
 *	Written by Junn Ohta (ohta@src.ricoh.co.jp). Public Domain.
 *      Modified by yamagata@nwgpc.kek.jp on 2000/04/13
 */

char	*progname = "squeeze";
char	*version = "1.1";
char	*date = "2000/04/13";
char	*author = "Junn Ohta (ohta@src.ricoh.co.jp)";

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

/*
 * 書籍構成要素種別
 */
#define	T_UNKNOWN	0	/* 不明				*/
#define	T_TEXT		1	/* 本文、メニュー、見出し	*/
#define	T_INDEX		2	/* インデックス			*/
#define	T_DATA		3	/* 音声、図版、外字		*/
#define	T_CINFO		4	/* 複合検索管理情報		*/

/*
 * インデックス種別
 */
#define	IT_UNKNOWN	0	/* 不明				*/
#define	IT_DIRECT	1	/* 見出しを経由せず直接本文へ	*/
#define	IT_NORMAL	2	/* 見出し経由で本文へ		*/
#define	IT_COND		3	/* 条件検索			*/
#define	IT_ITEM		4	/* 項目別条件検索		*/

/*
 * 書籍構成要素属性表
 */
typedef struct itype_t {
    byte	idmin;
    byte	idmax;
    int		must;	 /* 新しい書籍にコピーするか? */
    int		type;
    int		idxtype;
} ITYPE_T;

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

/*
 * 書籍構成要素再配置用のテーブル
 */
typedef	struct slot_t {
    byte	id;		/* 構成要素識別子		*/
    dword	topblk;		/* 構成要素の先頭ブロック	*/
    dword	endblk;		/* 構成要素の最終ブロック	*/
    dword	blks;		/* 構成要素のブロック数		*/
    dword	ntopblk;	/* コピー先の先頭ブロック	*/
} SLOT_T;

long	blkno;
uchr	*bookfile;
uchr	*nbookfile;
uchr	*logfile;
SLOT_T	*slot;			/* 再配置計算用テーブル		*/
int	slots;			/* 再配置計算用テーブルの要素数	*/
long	totalblks;
byte	buf[BLKSIZ];
byte	tmpbuf[BLKSIZ];
int	tty;

int	EBGmode = 0;

int	main();
void	usage();
void	init();
void	term();
int	squeeze();
int	nonzero();
ulng	val();
INFO_T	*mknewinfo();
int	checkslot();
long	reloc();
int	idmust();
int	idtype();
int	ididxtype();
int	markdel();
int	hex();
int	copybook();
void	putvoid();
int	copytext();
int	copyindex();
int	copydata();

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':
	    if (markdel(&av[0][2]) == ERR)
		usage();
	    break;
	default:
	    usage();
	}
	ac--, av++;
    }
    if (ac != 2)
	usage();
    bookfile = av[0];
    nbookfile = av[1];
    init();
    blkno = 1L;
    ret = squeeze(bookfile, blkno, nbookfile);
    term();
    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 [-g] [-d<削除IDリスト>]", progname);
    fprintf(stderr, " <入力書籍ファイル> <出力書籍ファイル>\n\n");
    fprintf(stderr, "オプション:\n");
    fprintf(stderr, "    -g: EBG 専用モードにする\n");
    fprintf(stderr, "    -d: 指定IDの構成要素を取り除く\n");
    exit(1);
}

void
init()
{
    time_t	t;
    FILE	*fp;

    tty = isatty(fileno(stderr));
    logfile = (uchr *)malloc(strlen(progname) + strlen(".log") + 1);
    sprintf(logfile, "%s.log", progname);
    if ((fp = fopen(logfile, "a")) == NULL)
	return;
    time(&t);
    fprintf(fp, "処理開始: %s\n", ctime(&t));
    fclose(fp);
}

void
term()
{
    time_t	t;
    FILE	*fp;

    if ((fp = fopen(logfile, "a")) == NULL)
	return;
    time(&t);
    fprintf(fp, "\n処理終了: %s\n", ctime(&t));
    fclose(fp);
}

#ifdef VARARGS
#include <varargs.h>
void
log(va_alist)
va_dcl
#else
#include <stdarg.h>
void
log(uchr *fmt, ...)
#endif
{
    FILE	*fp;
#ifdef VARARGS
    va_list va;
    uchr *fmt;
    va_start(va);
    fmt = va_arg(va, uchr *);
#else
    va_list va;
    va_start(va, fmt);
#endif
    vfprintf(stderr, fmt, va);
    if ((fp = fopen(logfile, "a")) != NULL) {
	vfprintf(fp, fmt, va);
	fclose(fp);
    }
    va_end(va);
}

int
squeeze(bookfile, blkno, nbookfile)
char	*bookfile, *nbookfile;
long	blkno;
{
    int		i;
    INFO_T	*infop, *ninfop;
    SLOT_T	*slotp;

    if (open_book(bookfile) == ERR) {
	log("%s がオープンできません\n", bookfile);
	return ERR;
    }
    if ((infop = getinfo(blkno)) == NULL) {
	log("書籍管理情報が取得できません\n");
	return ERR;
    }
    ninfop = mknewinfo(infop);
    if (ninfop == NULL) {
	log("メモリーが足りません\n");
	return ERR;
    }
    log("入力ファイル = %s\n", bookfile);
    log("出力ファイル = %s\n\n", nbookfile);
    log("《再配置プラン》\n\n");
    log("ID 開始ブロック 終了ブロック    サイズ  再配置先\n");
    slotp = slot;
    for (i = 0; i < slots; i++) {
	log("%02X     %8lu     %8lu  %8lu  %8lu\n",
	    slotp->id, slotp->topblk, slotp->endblk,
	    slotp->blks, slotp->ntopblk);
	slotp++;
    }
    log("\n");
    if (checkslot() == ERR) {
	log("書籍ファイルの再構成を中止しました\n");
	return ERR;
    }
    if (open_newbook(nbookfile) == ERR) {
	log("%s がオープンできません\n", nbookfile);
	return ERR;
    }
    if (putinfo(ninfop, 1L) == ERR) {
	log("書籍管理情報が書き込めません\n");
	return ERR;
    }
    if (copybook() == ERR) {
	log("書籍ファイルの再構成を中止しました\n");
	return ERR;
    }
    close_book();
    if (close_newbook() == ERR) {
	log("書籍ファイルがクローズできません\n");
	return ERR;
    }
    return OK;
}

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;
}

INFO_T *
mknewinfo(infop)
INFO_T	*infop;
{
    int		i, j, k, m, n;
    int		items, cents, citems, cinfos;
    long	topblk;
    INFO_T	*ninfop;
    ITEM_T	*itemp, *nitemp;
    CINFO_T	*cinfop, *ncinfop;
    CENT_T	*centp, *ncentp;
    CITEM_T	*citemp, *ncitemp;

    /*
     * コピーする構成要素の数(items)と
     * 複合検索管理情報の数(cinfos)と
     * 再配置計算用テーブルの要素数(slots)を数える。
     * 構成要素が複合検索管理情報(ID_CINFO)の場合、
     * itemsには1、slotsには下位構成要素の総数が加算される。
     * 複合検索の下位構成要素は重複することがあるが、
     * ここでは重複してもかまわずカウントする。
     */
    items = 0;
    cinfos = 0;
    slots = 0;
    itemp = infop->item;
    for (i = 0; i < infop->items; i++) {
	if (!idmust(itemp->itemid)) {
	    itemp++;
	    continue;
	}
	if (itemp->itemid != ID_CINFO) {
	    items++;
	    slots++;
	    itemp++;
	    continue;
	}
	items++;
	cinfos++;
	cinfop = itemp->cinfo;
	centp = cinfop->cent;
	for (j = 0; j < cinfop->cents; j++) {
	    citemp = centp->citem;
	    for (k = 0; k < centp->citems; k++) {
		slots++;
		citemp++;
	    }
	    centp++;
	}
	itemp++;
    }
    /*
     * 構成要素情報をコピーし、複合検索管理情報を再配置する。
     * 書籍管理情報は最初のブロックに書き込まれるので、
     * 複合検索管理情報は2ブロックめ以降に再配置される。
     */
    ninfop = (INFO_T *)malloc(sizeof(INFO_T));
    if (ninfop == NULL)
	return NULL;
    *ninfop = *infop;
    ninfop->items = items;
    nitemp = (ITEM_T *)malloc(sizeof(ITEM_T) * items);
    if (nitemp == NULL)
	return NULL;
    ninfop->item = nitemp;
    topblk = 2L;	/* 複合検索管理情報の開始ブロック */
    itemp = infop->item;
    nitemp = ninfop->item;
    for (i = 0; i < infop->items; i++) {
	if (!idmust(itemp->itemid)) {
	    itemp++;
	    continue;
	}
	*nitemp = *itemp;
	if (itemp->itemid != ID_CINFO) {
	    /*
	     * 複合検索管理情報以外の構成要素
	     */
	    itemp++;
	    nitemp++;
	    continue;
	}
	/*
	 * 複合検索管理情報
	 */
	nitemp->topblk = topblk++;
	cinfop = itemp->cinfo;
	ncinfop = (CINFO_T *)malloc(sizeof(CINFO_T));
	if (ncinfop == NULL)
	    return NULL;
	nitemp->cinfo = ncinfop;
	*ncinfop = *cinfop;
	centp = cinfop->cent;
	ncentp = (CENT_T *)malloc(sizeof(CENT_T) * ncinfop->cents);
	if (ncentp == NULL)
	    return NULL;
	ncinfop->cent = ncentp;
	for (j = 0; j < cinfop->cents; j++) {
	    *ncentp = *centp;
	    citemp = centp->citem;
	    ncitemp = (CITEM_T *)malloc(sizeof(CITEM_T) * ncentp->citems);
	    if (ncitemp == NULL)
		return NULL;
	    ncentp->citem = ncitemp;
	    for (k = 0; k < centp->citems; k++) {
		*ncitemp = *citemp;
		citemp++;
		ncitemp++;
	    }
	    centp++;
	    ncentp++;
	}
	itemp++;
	nitemp++;
    }
    /*
     * 再配置計算用テーブルを作ると同時に、
     * 複合検索管理情報以外の構成要素も再配置する。
     */
    slot = (SLOT_T *)malloc(sizeof(SLOT_T) * slots);
    if (slot == NULL)
	return NULL;
    slots = 0;	/* 重複を除いたslotsの個数を再計算する */
    nitemp = ninfop->item;
    for (i = 0; i < ninfop->items; i++) {
	if (nitemp->itemid != ID_CINFO) {
	    /*
	     * 複合検索管理情報以外の構成要素
	     */
	    for (m = 0; m < slots; m++) {
		if (slot[m].topblk == nitemp->topblk) {
		    nitemp->topblk = slot[m].ntopblk;
		    break;
		}
	    }
	    if (m == slots) {
		slot[slots].id = nitemp->itemid;
		slot[slots].topblk = nitemp->topblk;
		slot[slots].endblk = nitemp->topblk + nitemp->blks - 1;
		slot[slots].blks = nitemp->blks;
		slot[slots].ntopblk = topblk;
		nitemp->topblk = topblk;
		topblk += nitemp->blks;
		slots++;
	    }
	    nitemp++;
	    continue;
	}
	/*
	 * 複合検索管理情報
	 */
	ncinfop = nitemp->cinfo;
	ncentp = ncinfop->cent;
	for (j = 0; j < ncinfop->cents; j++) {
	    ncitemp = ncentp->citem;
	    for (k = 0; k < ncentp->citems; k++) {
		for (m = 0; m < slots; m++) {
		    if (slot[m].topblk == ncitemp->ctopblk) {
			ncitemp->ctopblk = slot[m].ntopblk;
			break;
		    }
		}
		if (m == slots) {
		    slot[slots].id = ncitemp->citemid;
		    slot[slots].topblk = ncitemp->ctopblk;
		    slot[slots].endblk = ncitemp->ctopblk + ncitemp->cblks - 1;
		    slot[slots].blks = ncitemp->cblks;
		    slot[slots].ntopblk = topblk;
		    ncitemp->ctopblk = topblk;
		    topblk += ncitemp->cblks;
		    slots++;
		}
		ncitemp++;
	    }
	    ncentp++;
	}
	nitemp++;
    }
    return ninfop;
}

int
checkslot()
{
    int		i, err;

    err = 0;
    for (i = 0; i < slots; i++) {
	if (idtype(slot[i].id) == T_UNKNOWN) {
	    log("構造が不明の書籍構成要素があります(ID=%02X)\n", slot[i].id);
	    err++;
	}
	if (idtype(slot[i].id) == T_INDEX &&
	    ididxtype(slot[i].id) == IT_UNKNOWN) {
	    log("構造が不明のインデックスがあります(ID=%02X)\n", slot[i].id);
	    err++;
	}
    }
    if (err)
	return ERR;
    return OK;
}

long
reloc(blk)
long	blk;
{
    int		i;
    SLOT_T	*slotp;

    slotp = slot;
    for (i = 0; i < slots; i++) {
	if (blk >= slotp->topblk && blk <= slotp->endblk)
	    return blk - slotp->topblk + slotp->ntopblk;
	slotp++;
    }
    return 0L;
}

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
idtype(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].type;
    }
    return T_UNKNOWN;
}

int
ididxtype(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].idxtype;
    }
    return IT_UNKNOWN;
}

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;
}

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;
}

int
copybook()
{
    int		i, ret;
    SLOT_T	*p;

    if (tty) {
	fprintf(stderr, "処理中です(0%%)");
	fflush(stderr);
    }
    totalblks = slot[slots-1].ntopblk + slot[slots-1].blks - 1;
    p = slot;
    for (i = 0; i < slots; i++) {
	if (locate_block(p->topblk) == ERR) {
	    if (tty)
		putc('\n', stderr);
	    log("ファイルの読み込みに失敗しました\n");
	    return ERR;
	}
	if (locate_newblock(p->ntopblk) == ERR) {
	    if (tty)
		putc('\n', stderr);
	    log("ファイルの書き込みに失敗しました\n");
	    return ERR;
	}
	switch (idtype(p->id)) {
	case T_TEXT:
	    ret = copytext(p->id, p->topblk, p->blks);
	    break;
	case T_INDEX:
	    ret = copyindex(p->id, p->topblk, p->blks, ididxtype(p->id));
	    break;
	case T_DATA:
	    ret = copydata(p->id, p->topblk, p->blks);
	    break;
	}
	if (ret == ERR)
	    return ERR;
	p++;
    }
    if (tty)
	fprintf(stderr, "\r終了しました    \n");
    else
	log("終了しました\n");
    return OK;
}

void
putvoid(n)
int	n;
{
    if (n >= 2) {
	/*
	 * "→□"を書き込む
	 */
	if (!EBGmode) {
	    putword(0x222a);
	    putword(0x2222);
	} else {
	    putbyte('-');
	    putbyte('>');
	    putbyte('[');
	    putbyte(']');
	}
	n -= 2;
    }
    if (n >= 3 && n & 1) {
	putword(0x1fe0);	/* 拡張強調表示開始 */
	putword(0x0001);
	putword(0x1fe1);	/* 拡張強調表示終了 */
	n -= 3;
    }
    while (n >= 2) {
	putword(0x1f04);	/* 半角開始 */
	putword(0x1f05);	/* 半角終了 */
	n -= 2;
    }
    while (n--)
	if (!EBGmode) {
	    putword(0x2121);	/* 空白 */
	} else {
	    putbyte(' ');	/* 空白 */
	    putbyte(' ');	/* 空白 */
	}
}

int
copytext(itemid, topblk, blks)
int	itemid;
long	topblk, blks;
{
    int		i, pc, npc, curoff, erroff;
    long	err, rel, curblk, endblk, errblk;
    long	blk1, blk2, off1, off2, bcd1, bcd2;
    word	w, w1, w2;
    byte	*p;
#ifdef TEST
    long	nerr = 0L;
#endif

    if (tty) {
	pc = -1;
	putc('\r', stderr);
    }
    log("ID=%02X, 種別=テキスト, 開始ブロック=%ld, ブロック数=%ld\n",
	itemid, topblk, blks);
    err = 0L;
    rel = 0L;
    reset_error();
    endblk = topblk + blks - 1;
    for (;;) {
	if (get_error() > 0) {
	    if (tty)
		putc('\n', stderr);
	    log("ファイルの書き込み中にエラーが発生しました\n");
	    return ERR;
	}
	curblk = cur_block();
	curoff = cur_off();
	if (curblk > endblk || curblk == endblk && curoff == BLKSIZ)
	    break;
	if (tty) {
	    npc = (int)(cur_newblock() * 100 / totalblks);
	    if (npc > pc) {
		fprintf(stderr, "\r処理中です(%d%%)", npc);
		fflush(stderr);
		pc = npc;
	    }
	}
	if (!EBGmode) {
	    w = getword();
	} else {
	    w = getbyte();
	}
    again:
	if (!EBGmode) {
	    if (w >= 0x2121 && w <= 0x7e7e || w >= 0xa121 && w <= 0xfe7e) {
		/*
		 * JIS文字。仕様上の文字範囲は以下の通りのはず。
		 *   通常文字: 2121〜7426
		 *   外字: a121〜fe7e
		 * しかし三省堂ワードハンターの現代国語辞典に7671、
		 * 電子ブック版研究社新英和・和英中辞典に7440がある。
		 */
		putword(w);
		w = getword();
		goto again;
	    }
	} else {
	    if (w == 0) {
	    } else if (w == 0x1f) {
		w = w << 8 | getbyte();
	    } else {
		putbyte(w & 0xff);
		if (0 < w && w < 0x1f) { /* EBG gaiji */
		    w = getbyte();
		    putbyte(w & 0xff);
		}
		w = getbyte();
		goto again;
	    }
	}

	if (w == 0x0000) {
	    /*
	     * パディング
	     */
	    if (!EBGmode) {
		putword(w);
	    } else {
		putbyte(w);
	    }
	    continue;
	}
	/*
	 * 表示制御記述子
	 */
	if ((w & 0xff00) == 0x1f00) {
	    switch (w & 0x00ff) {
	    case 0x09:	/* 字下げ指定 */
	    case 0x1a:	/* JIS X4081附属書の指示による */
	    case 0x1b:	/* JIS X4081附属書の指示による */
	    case 0x1c:	/* JIS X4081附属書の指示による */
	    case 0x1d:	/* JIS X4081附属書の指示による */
	    case 0x1e:	/* JIS X4081附属書の指示による */
	    case 0x1f:	/* JIS X4081附属書の指示による */
	    case 0x41:	/* 検索キー記述子 */
	    case 0x45:	/* 図版データ群識別子 (DIC 0.23による) */
	    case 0xe0:	/* 拡張強調表示開始指定 */
	    case 0xe2:	/* 保護開始指定 (DIC 0.23による) */
		/*
		 * 直後の1WORDをそのままコピーする。
		 * 1f45は電子ブック版広辞苑第四版に出てくる。
		 */
		putword(w);
		w1 = getword();
		putword(w1);
		break;
	    case 0x31:	/* 図版見出し中の指示開始? */
	    case 0x32:	/* 電子ブック拡張モノクロ図版 */
	    case 0x42:	/* 別項目参照記述子 */
	    case 0x44:	/* 図版データ識別子 (DIC 0.23による) */
	    case 0x46:	/* 図版メニュー識別子 (DIC 0.23による) */
	    case 0x49:	/* 未確認: 表? (cdrom2による) */
	    case 0x4b:	/* 未確認: 図形枠? (cdrom2による) */
	    case 0x4c:	/* 未確認: 図形枠? (cdrom2による) */
	    case 0x4e:	/* 未確認: ポインタ? (cdrom2による) */
	    case 0x4f:	/* 未確認: ポインタ? (cdrom2による) */
		/*
		 * 終了識別子までを一時的に保存しておき、
		 * 終了識別子の直後にあるBCD4+BCD2を見て、
		 * 参照先がなければ全体を「→□」で置き換える。
		 *
		 * 電子ブック版研究社新英和・和英中辞典では
		 * 1f42の終了識別子1f62でオフセットが
		 * 2048以上になっている箇所がある。
		 * データを見るとオフセットから2048を引いて
		 * 次のブロックを参照するという方法で
		 * つじつまがあっているようだ。
		 */
		p = tmpbuf;
		*p++ = w >> 8;
		*p++ = w & 0xff;
		w1 = w + 0x0020;

		if (!EBGmode) {
		    while ((w = getword()) != w1) {
			*p++ = w >> 8;
			*p++ = w & 0xff;
		    }
		} else {
		    int ww;

		    while (1) {
			w = getbyte();
			if (w == 0x1f) {
			    ww = getbyte();
			    if (w << 8 | ww == w1) {
				w = w1;
				break;
			    } else {
				*p++ = w;
				*p++ = ww;
			    }
			} else {
			    *p++ = w;
			}
		    }
		}

		blk1 = getbcd(4);
		off1 = getbcd(2);
		if (blk1 < 0 || off1 < 0 || off1 >= BLKSIZ) {
		    err++;
		    errblk = cur_block();
		    erroff = cur_off();
#ifdef TEST
		    if (tty)
			putc('\r', stderr);
		    log("err: pos=%lu:%d\n", cur_block(), cur_off());
#endif
		}
		blk1 = reloc(blk1);
		if (blk1 == 0L) {
		    putvoid((p - tmpbuf) / 2 + 4);
		} else {
		    putbytes(tmpbuf, p - tmpbuf);
		    putword(w);
		    putbcd(blk1, 4);
		    putbcd(off1, 2);
		    rel++;
		}
		break;
	    case 0x33:	/* 電子ブック音声 (ndtpdによる) */
		/*
		 * ハードディスク上では再生不能なので
		 * 全体を「→□」で置き換える。
		 */
		i = 1;
		while ((w = getword()) != 0x1f53)
		    i++;
		i++;
		w = getword();
		/*
		 * 電子ブック版広辞苑第四版の図版見出しを見る限り、
		 * ここに来るのはBCD4+BCD2ではないようだ。
		 */
		bcd1 = getbcd(3);
		bcd2 = getbcd(3);
		if (bcd1 < 0 || bcd2 < 0) {
		    err++;
		    errblk = cur_block();
		    erroff = cur_off();
#ifdef TEST
		    if (tty)
			putc('\r', stderr);
		    log("err: pos=%lu:%d\n", cur_block(), cur_off());
#endif
		}
		i += 4;
		putvoid(i);
		break;
	    case 0x48:	/* 音声データ識別子 (DIC 0.23による) */
		/*
		 * 1f48直後の5WORDはCDトラックを参照している。
		 * ハードディスク上では再生不能なので
		 * 1f68までの全体を「→□」で置き換える。
		 * 1f68の後にはポインターは来ない。
		 * 1f48を使っている例はEPWING版大辞林にある。
		 */
		i = 1;
		while ((w = getword()) != 0x1f68)
		    i++;
		i++;
		putvoid(i);
		break;
	    case 0x4a:	/* EPWING PCM音声 */
		/*
		 * 1f4a+WORD+WORD+BCD4+BCD2+BCD4+BCD2+〜+1f6a
		 * という構造をしている。
		 */
		w1 = getword();
		w2 = getword();
		blk1 = getbcd(4);
		off1 = getbcd(2);
		blk2 = getbcd(4);
		off2 = getbcd(2);
		if (blk1 < 0 || off1 < 0 || off1 >= BLKSIZ ||
		    blk2 < 0 || off2 < 0 || off2 >= BLKSIZ) {
		    err++;
		    errblk = cur_block();
		    erroff = cur_off();
#ifdef TEST
		    if (tty)
			putc('\r', stderr);
		    log("err: pos=%lu:%d\n", cur_block(), cur_off());
#endif
		}
		blk1 = reloc(blk1);
		blk2 = reloc(blk2);
		if (blk1 == 0L || blk2 == 0L) {
		    i = 9;
		    while (getword() != 0x1f6a)
			i++;
		    i++;
		    putvoid(i);
		} else {
		    putword(w);
		    putword(w1);
		    putword(w2);
		    putbcd(blk1, 4);
		    putbcd(off1, 2);
		    putbcd(blk2, 4);
		    putbcd(off2, 2);
		    rel++;
		    rel++;
		    while ((w = getword()) != 0x1f6a)
			putword(w);
		    putword(0x1f6a);
		}
		break;
	    case 0x4d:	/* カラー図版 */
		/*
		 * ここは非常にあやしい。実例はいまのところ
		 * EPWING版広辞苑第四版の本文にしか見かけない。
		 * 例としては「アイガー」で2枚のカラー図版を
		 * 参照しているところがある。すべての箇所が
		 *   1f4d+0009+0000*5+BCD4+BCD2+ラベル+1f6d
		 * という形であり、それ以外の組み合わせはない。
		 * とりあえず
		 *   1f4d+WORD*6+BCD4+BCD2+ラベル+1f6d
		 * という形式であると解釈しておく。
		 */
		p = tmpbuf;
		*p++ = w >> 8;
		*p++ = w & 0xff;
		for (i = 0; i < 6; i++) {
		    w = getword();
		    *p++ = w >> 8;
		    *p++ = w & 0xff;
		}
		blk1 = getbcd(4);
		off1 = getbcd(2);
		if (blk1 < 0 || off1 < 0 || off1 >= BLKSIZ) {
		    err++;
		    errblk = cur_block();
		    erroff = cur_off();
#ifdef TEST
		    if (tty)
			putc('\r', stderr);
		    log("err: pos=%lu:%d\n", cur_block(), cur_off());
#endif
		}
		blk1 = reloc(blk1);
		if (blk1 == 0L) {
		    i = 1 + 6 + 3;
		    while (getword() != 0x1f6d)
			i++;
		    i++;
		    putvoid(i);
		} else {
		    putbytes(tmpbuf, p - tmpbuf);
		    putbcd(blk1, 4);
		    putbcd(off1, 2);
		    rel++;
		    while ((w = getword()) != 0x1f6d)
			putword(w);
		    putword(0x1f6d);
		}
		break;
	    case 0x62:	/* 別項目参照終了記述子 */
	    case 0x63:	/* メニュー終了記述子 */
		/*
		 * アスキー辞・典・盤97のマイペディア97で、
		 * 「日本」の別項目参照中、「→ポツダム宣言」
		 * の直後に「ヌ→二・一スト」という項目がある。
		 * どうやら1f42が254cに化けているようだ。
		 * case 0x42で1f62まで読んで処理しているのに
		 * case 0x62があるのは、これに対応するため。
		 * (しかしデータ化けを訂正するわけではない。)
		 */
		putword(w);
		blk1 = getbcd(4);
		off1 = getbcd(2);
		if (blk1 < 0 || off1 < 0 || off1 >= BLKSIZ) {
		    err++;
		    errblk = cur_block();
		    erroff = cur_off();
#ifdef TEST
		    if (tty)
			putc('\r', stderr);
		    log("err: pos=%lu:%d\n", cur_block(), cur_off());
#endif
		}
		blk1 = reloc(blk1);
		putbcd(blk1, 4);
		putbcd(off1, 2);
		rel++;
		break;
	    case 0x14:	/* 色指定開始指定 (DIC 0.23による) */
	    case 0x67:	/* 色見本メニュー終了識別子 (DIC 0.23による) */
		do {
		    putword(w);
		    w = getword();
		} while ((w & 0xff00) == 0x1e00);
		goto again;
	    case 0x15:	/* 色指定終了指定 (DIC 0.23による) */
		/*
		 * EPWNIG版広辞苑第四版CD-ROM(カラー)版の
		 * 条件検索で「あんりょくしょく」を検索すると
		 * 「オリーブ色」の見出し末尾の「】」の後で、
		 * 1f15の直後にどういうわけか0260が出てくる。
		 * ことといVer.2、富士通CD辞書検索V1.3L12、
		 * OASYS-CDView/Win V3.0L10では表示が化けないが、
		 * DDwin、ViewIng95、WordEngine2では化ける。
		 * 広辞苑のバグか?
		 */
		putword(w);
		w = getword();
		if ((w & 0xff00) == 0x1f00)
		    goto again;
		putword(w);
		break;
	    default:
		putword(w);
		break;
	    }
	    continue;
	}
	/*
	 * 電子ブック版三省堂マック辞典の英和・和英辞典
	 * 「センチュリ+ビジネス+クラウン」では書籍構成要素
	 * 0F(図版一覧)の中にD1(拡張モノクロ図版)のデータも
	 * ごちゃまぜで入っているようだ。たいていのデータは
	 * エラーとしてここに落ちてくるが、データの一部が
	 * 表示制御記述子とたまたま一致してしまうと、開始・
	 * 終了記述子の対応がとれずにプログラムが異常終了
	 * してしまう可能性がある。うまい対策がないので、
	 * ここでは放置しておく。
	 */
	putword(w);
#ifdef TEST
	if (nerr++ < 20L) {
	    if (tty)
		putc('\r', stderr);
	    log("ERR: word=%04X, pos=%lu:%d(%04x)\n",
		w, curblk, curoff, curoff);
	}
#endif
	err++;
	errblk = cur_block();
	erroff = cur_off();
    }
    if (err) {
	if (tty)
	    putc('\n', stderr);
	log("未知の表示制御構造がありました(個数=%ld, 最終位置=%lu:%d)\n",
	    err, errblk, erroff);
    }
    return OK;
}

int
copyindex(itemid, topblk, blks, idxtype)
int	itemid;
long	topblk, blks;
int	idxtype;
{
    int		i, pc, npc, id, eid;
    int		off1, off2, erroff;
    long	n, err, ref, rel, errblk;
    long	blk1, blk2;
    word	len, cnt, num, dum;

    if (tty) {
	pc = -1;
	putc('\r', stderr);
    }
    log("ID=%02X, 種別=インデックス, 開始ブロック=%ld, ブロック数=%ld\n",
	itemid, topblk, blks);
    err = 0L;
    ref = 0L;
    rel = 0L;
    reset_error();
    n = cur_newblock();
    while (blks--) {
	if (tty) {
	    npc = (int)(n++ * 100 / totalblks);
	    if (npc > pc) {
		fprintf(stderr, "\r処理中です(%d%%)", npc);
		fflush(stderr);
		pc = npc;
	    }
	}
	id = getbyte();
	len = getbyte();
	cnt = getword();
	putbyte(id);
	putbyte(len);
	putword(cnt);

	if ((id & 0x80) == 0x00) {
	    /*
	     * 上位インデックスブロック
	     */
	    for (i = 0; i < cnt; i++) {
		getbytes(buf, len);
		blk1 = getdword();
		blk1 = reloc(blk1);
		if (blk1 == 0L) {
		    errblk = cur_block();
		    erroff = cur_off();
		    ref++;
		}
		putbytes(buf, len);
		putdword(blk1);
		rel++;
	    }
	    goto nextblock;
	}

	/*
	 * 最下位インデックスブロック
	 */
	if ((id & 0x10) == 0x00) {
	    /*
	     * 一般型エントリ
	     */
	    switch (idxtype) {
	    case IT_DIRECT:
		for (i = 0; i < cnt; i++) {
		    len = getbyte();
		    getbytes(buf, len);
		    blk1 = getdword();
		    off1 = getword();
		    if (off1 >= BLKSIZ)
			err++;
		    blk1 = reloc(blk1);
		    if (blk1 == 0L) {
			errblk = cur_block();
			erroff = cur_off();
			ref++;
		    }
		    putbyte(len);
		    putbytes(buf, len);
		    putdword(blk1);
		    putword(off1);
		    rel++;
		}
		break;

	    default:
		for (i = 0; i < cnt; i++) {
		    len = getbyte();
		    getbytes(buf, len);
		    blk1 = getdword();
		    off1 = getword();
		    blk2 = getdword();
		    off2 = getword();
		    if (off1 > BLKSIZ || off2 >= BLKSIZ) {
			/*
			 * 電子ブック版研究社新英和・和英中辞典では
			 * オフセットoff1が2048になっている箇所がある。
			 * 次のブロックの先頭を参照すると解釈すれば
			 * つじつまがあっているので、エラーにはしない。
			 * これに対応するため、ここだけエラーの範囲を
			 * 「>= BLKSIZ」ではなく「> BLKSIZ」とする。
			 */
			err++;
		    }
		    blk1 = reloc(blk1);
		    blk2 = reloc(blk2);
		    if (blk1 == 0L || blk2 == 0L) {
			errblk = cur_block();
			erroff = cur_off();
			ref++;
		    }
		    putbyte(len);
		    putbytes(buf, len);
		    putdword(blk1);
		    putword(off1);
		    putdword(blk2);
		    putword(off2);
		    rel++;
		    rel++;
		}
		break;
	    }
	    goto nextblock;
	}

	/*
	 * 集団概念付きエントリ
	 */
	switch (idxtype) {
	case IT_DIRECT:
	    for (i = 0; i < cnt; i++) {
		eid = getbyte();
		putbyte(eid);
		if (eid == 0x00) {
		    /*
		     * 基本エントリ
		     */
		    len = getbyte();
		    getbytes(buf, len);
		    blk1 = getdword();
		    off1 = getword();
		    if (off1 >= BLKSIZ)
			err++;
		    blk1 = reloc(blk1);
		    if (blk1 == 0L) {
			errblk = cur_block();
			erroff = cur_off();
			ref++;
		    }
		    putbyte(len);
		    putbytes(buf, len);
		    putdword(blk1);
		    putword(off1);
		    rel++;
		} else if (eid == 0x80) {
		    /*
		     * 集団エントリ
		     */
		    len = getbyte();
		    num = getword();
		    getbytes(buf, len);
		    putbyte(len);
		    putword(num);
		    putbytes(buf, len);
		} else if (eid == 0xc0) {
		    /*
		     * メンバ情報
		     */
		    len = getbyte();
		    getbytes(buf, len);
		    blk1 = getdword();
		    off1 = getword();
		    if (off1 >= BLKSIZ)
			err++;
		    blk1 = reloc(blk1);
		    if (blk1 == 0L) {
			errblk = cur_block();
			erroff = cur_off();
			ref++;
		    }
		    putbyte(len);
		    putbytes(buf, len);
		    putdword(blk1);
		    putword(off1);
		    rel++;
		} else {
		    err++;
		    break;
		}
	    }
	    break;

	case IT_NORMAL:
	    for (i = 0; i < cnt; i++) {
		eid = getbyte();
		putbyte(eid);
		if (eid == 0x00) {
		    /*
		     * 基本エントリ
		     */
		    len = getbyte();
		    getbytes(buf, len);
		    blk1 = getdword();
		    off1 = getword();
		    blk2 = getdword();
		    off2 = getword();
		    if (off1 >= BLKSIZ || off2 >= BLKSIZ)
			err++;
		    blk1 = reloc(blk1);
		    blk2 = reloc(blk2);
		    if (blk1 == 0L || blk2 == 0L) {
			errblk = cur_block();
			erroff = cur_off();
			ref++;
		    }
		    putbyte(len);
		    putbytes(buf, len);
		    putdword(blk1);
		    putword(off1);
		    putdword(blk2);
		    putword(off2);
		    rel++;
		} else if (eid == 0x80) {
		    /*
		     * 集団エントリ
		     */
		    len = getbyte();
		    num = getword();
		    getbytes(buf, len);
		    putbyte(len);
		    putword(num);
		    putbytes(buf, len);
		} else if (eid == 0xc0) {
		    /*
		     * メンバ情報
		     */
		    len = getbyte();
		    getbytes(buf, len);
		    blk1 = getdword();
		    off1 = getword();
		    blk2 = getdword();
		    off2 = getword();
		    if (off1 >= BLKSIZ || off2 >= BLKSIZ)
			err++;
		    blk1 = reloc(blk1);
		    blk2 = reloc(blk2);
		    if (blk1 == 0L || blk2 == 0L) {
			errblk = cur_block();
			erroff = cur_off();
			ref++;
		    }
		    putbyte(len);
		    putbytes(buf, len);
		    putdword(blk1);
		    putword(off1);
		    putdword(blk2);
		    putword(off2);
		    rel++;
		} else {
		    err++;
		    break;
		}
	    }
	    break;

	case IT_COND:
	    for (i = 0; i < cnt; i++) {
		eid = getbyte();
		putbyte(eid);
		if (eid == 0x00) {
		    /*
		     * 基本エントリ
		     */
		    len = getbyte();
		    getbytes(buf, len);
		    blk1 = getdword();
		    off1 = getword();
		    blk2 = getdword();
		    off2 = getword();
		    if (off1 >= BLKSIZ || off2 >= BLKSIZ)
			err++;
		    blk1 = reloc(blk1);
		    blk2 = reloc(blk2);
		    if (blk1 == 0L || blk2 == 0L) {
			errblk = cur_block();
			erroff = cur_off();
			ref++;
		    }
		    putbyte(len);
		    putbytes(buf, len);
		    putdword(blk1);
		    putword(off1);
		    putdword(blk2);
		    putword(off2);
		    rel++;
		} else if (eid == 0x80) {
		    /*
		     * 集団エントリ
		     */
		    len = getbyte();
		    dum = getword();
		    num = getword();
		    getbytes(buf, len);
		    blk1 = getdword();
		    off1 = getword();
		    if (off1 >= BLKSIZ)
			err++;
		    blk1 = reloc(blk1);
		    if (blk1 == 0L) {
			errblk = cur_block();
			erroff = cur_off();
			ref++;
		    }
		    putbyte(len);
		    putword(dum);
		    putword(num);
		    putbytes(buf, len);
		    putdword(blk1);
		    putword(off1);
		    rel++;
		} else if (eid == 0xc0) {
		    /*
		     * メンバ情報
		     */
		    blk1 = getdword();
		    off1 = getword();
		    if (off1 >= BLKSIZ)
			err++;
		    blk1 = reloc(blk1);
		    if (blk1 == 0L) {
			errblk = cur_block();
			erroff = cur_off();
			ref++;
		    }
		    putdword(blk1);
		    putword(off1);
		    rel++;
		} else {
		    err++;
		    break;
		}
	    }
	    break;

	case IT_ITEM:
	    for (i = 0; i < cnt; i++) {
		eid = getbyte();
		putbyte(eid);
		if (eid == 0x00) {
		    /*
		     * 基本エントリ
		     */
		    len = getbyte();
		    getbytes(buf, len);
		    blk1 = getdword();
		    off1 = getword();
		    blk2 = getdword();
		    off2 = getword();
		    if (off1 >= BLKSIZ || off2 >= BLKSIZ)
			err++;
		    blk1 = reloc(blk1);
		    blk2 = reloc(blk2);
		    if (blk1 == 0L || blk2 == 0L) {
			errblk = cur_block();
			erroff = cur_off();
			ref++;
		    }
		    putbyte(len);
		    putbytes(buf, len);
		    putdword(blk1);
		    putword(off1);
		    putdword(blk2);
		    putword(off2);
		    rel++;
		} else if (eid == 0x80) {
		    /*
		     * 集団エントリ
		     */
		    len = getbyte();
		    dum = getword();
		    num = getword();
		    getbytes(buf, len);
		    putbyte(len);
		    putword(dum);
		    putword(num);
		    putbytes(buf, len);
		} else if (eid == 0xc0) {
		    /*
		     * メンバ情報
		     */
		    blk1 = getdword();
		    off1 = getword();
		    blk2 = getdword();
		    off2 = getword();
		    if (off1 >= BLKSIZ || off2 >= BLKSIZ)
			err++;
		    blk1 = reloc(blk1);
		    blk2 = reloc(blk2);
		    if (blk1 == 0L || blk2 == 0L) {
			errblk = cur_block();
			erroff = cur_off();
			ref++;
		    }
		    putdword(blk1);
		    putword(off1);
		    putdword(blk2);
		    putword(off2);
		    rel++;
		} else {
		    err++;
		    break;
		}
	    }
	    break;
	}

    nextblock:
	if (err) {
	    if (tty)
		putc('\n', stderr);
	    log("インデックス構造に不整合があります(ブロック=%lu)\n",
		cur_block());
	    return ERR;
	}
	if (get_error() > 0 || write_newblock() == ERR) {
	    if (tty)
		putc('\n', stderr);
	    log("ファイルの書き込み中にエラーが発生しました\n");
	    return ERR;
	}
	read_block();
    }
    if (ref > 0) {
	if (tty)
	    putc('\n', stderr);
	log("参照先のないインデックスがあります(個数=%ld, 最終位置=%lu:%d)\n",
	    ref, errblk, erroff);
    }
    return OK;
}

int
copydata(itemid, topblk, blks)
int	itemid;
long	topblk, blks;
{
    int		pc, npc;
    long	n;

    if (tty) {
	pc = -1;
	putc('\r', stderr);
    }
    log("ID=%02X, 種別=データ, 開始ブロック=%ld, ブロック数=%ld\n",
	itemid, topblk, blks);
    reset_error();
    n = cur_newblock();
    while (blks--) {
	if (tty) {
	    npc = (int)(n++ * 100 / totalblks);
	    if (npc > pc) {
		fprintf(stderr, "\r処理中です(%d%%)", npc);
		fflush(stderr);
		pc = npc;
	    }
	}
	getbytes(buf, BLKSIZ);
	putbytes(buf, BLKSIZ);
	if (get_error() > 0) {
	    if (tty)
		putc('\n', stderr);
	    log("ファイルの書き込み中にエラーが発生しました\n");
	    return ERR;
	}
    }
    return OK;
}
