/*
 * 小学館ランダムハウス英語辞典検索ユーティリティー - csrd
 *
 *	Written by Junn Ohta (ohta@src.ricoh.co.jp), Public Domain.
 */

char	*progname = "csrd";
char	*version  = "1.0";
char	*date     = "1999/07/19";

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef UNIX
#include <unistd.h>
#endif
#ifdef MSDOS
#include <io.h>
#endif

typedef	unsigned char	uchr;
typedef	unsigned int	unt;

#ifndef O_BINARY
#define	O_BINARY	0
#endif

#define	OK		0
#define	ERR		(-1)

#define	TRUE		1
#define	FALSE		0

#define	SJIS1(c)	((c)>=0x81 && (c)<=0x9f || (c)>=0xe0 && (c)<=0xef)

#ifdef DEBUG
int	debug = FALSE;		/* デバッグフラグ		*/
int	debug2 = FALSE;		/* デバッグフラグ		*/
#endif

/* -------------------- ユーティリティー関数 -------------------- */ 

#define	POOLSIZ		1024	/* 文字列プールの割り当て単位	*/

uchr	*pool = NULL;		/* 文字列プール			*/
uchr	*pfree = NULL;		/* 文字列プールの空き領域先頭	*/

int	hexval();
uchr	*escchar();
uchr	*skipeq();
uchr	*skipsp();
uchr	*strpool();
void	bsltosl();
int	nspaces();
void	lower();

/*
 * hexval - 2桁の16進数が示す値を返す
 */
int
hexval(p)
uchr	*p;
{
    int		i, n;

    n = 0;
    for (i = 0; i < 2; i++) {
	n <<= 4;
	if (*p >= '0' && *p <= '9')
	    n += *p - '0';
	else if (*p >= 'a' && *p <= 'f')
	    n += *p - 'a' + 10;
	else if (*p >= 'A' && *p <= 'F')
	    n += *p - 'A' + 10;
	else
	    n = 0;
	p++;
    }
    return n;
}

/*
 * escchar - '\'でエスケープされた文字を調べ、次の位置を返す
 */
uchr *
escchar(str, cp)
uchr	*str;
int	*cp;
{
    uchr	*p;

    p = str;
    if (*p++ != '\\')
	return str;
    if (*p >= '0' && *p <= '7') {
	sscanf(p, "%3o", cp);
	return p + 3;
    }
    switch (*p) {
    case 'x':
	*cp = hexval(p + 1);
	p += 3;
	break;
    case 'n':
	*cp = '\n';
	p++;
	break;
    case 't':
	*cp = '\t';
	p++;
	break;
    default:
	*cp = *p++;
	break;
    }
    return p;
}

/*
 * skipeq - '='までを読み飛ばし、次の位置を返す
 */
uchr *
skipeq(str)
uchr	*str;
{
    while (*str && *str != '=')
	str++;
    if (*str == '=')
	str++;
    return str;
}

/*
 * skipsp - 空白を読み飛ばし、次の位置を返す
 */
uchr *
skipsp(str)
uchr	*str;
{
    while (*str == ' ' || *str == '\t')
	str++;
    return str;
}

/*
 * strpool - 文字列をプールに格納してそのアドレスを返す
 *	     (解放を考慮しないstrdup)
 */
uchr *
strpool(str)
uchr	*str;
{
    int		len;
    uchr	*p;

    if (str == NULL)
	return NULL;
    len = strlen(str) + 1;
    if (pool == NULL || pfree + len > pool + POOLSIZ) {
	pool = (uchr *)malloc(POOLSIZ); /* 以前のpoolの値は忘れる */
	if (pool == NULL)
	    return NULL;
	pfree = pool;
    }
    p = pfree;
    pfree += len;
    return strcpy(p, str);
}

/*
 * bsltosl - 文字列中のバックスラッシュ(\)をスラッシュ(/)に変換する
 */
void
bsltosl(str)
uchr	*str;
{
    if (str == NULL)
	return;
    while (*str) {
	if (*str == '\\')
	    *str = '/';
	if (SJIS1(*str))
	    str++;
	str++;
    }
}

/*
 * nspaces - 文字列の先頭にあるスペースの量を数える
 */
int
nspaces(str)
uchr	*str;
{
    int		n;

    if (str == NULL)
	return 0;
    n = 0;
    while (*str && isspace(*str)) {
	switch (*str++) {
	case ' ':
	    n++;
	    break;
	case '\t':
	    n = (n | 0x07) + 1;
	    break;
	case '\n':
	case '\r':
	    n = 0;
	    break;
	case '\b':
	    if (n > 0)
		n--;
	    break;
	}
    }
    return n;
}

/*
 * lower - 英文字列を小文字化する
 */
void
lower(str)
uchr	*str;
{
    if (str == NULL)
	return;
    while (*str) {
	if (isascii(*str) && isupper(*str))
	    *str = tolower(*str);
	str++;
    }
}

/* -------------------- バッファード入力 -------------------- */

#define	BBUFSIZ		4096

typedef	struct _bfile {
    int		fd;
    long	size;
    long	pos;
    uchr	*p, *ep;
    uchr	buf[BBUFSIZ];
} BFILE;

BFILE	*bopen();
long	btell();
int	bseek();
int	bread();
int	bgetc();
int	bgetcbw();
int	bclose();

/*
 * bopen - バッファード入力でファイルをオープンする
 *	   bseek()で位置づけをするまで読み込みはできない
 */
BFILE *
bopen(file)
char	*file;
{
    int		fd;
    BFILE	*bp;
    struct stat	st;

    if ((fd = open(file, O_RDONLY|O_BINARY)) < 0)
	return NULL;
    if (fstat(fd, &st) < 0) {
	close(fd);
	return NULL;
    }
    bp = (BFILE *)malloc(sizeof(BFILE));
    if (bp == NULL) {
	close(fd);
	return NULL;
    }
    bp->fd = fd;
    bp->size = st.st_size;
    bp->pos = 0L;
    bp->p = bp->ep = NULL;
    return bp;
}

/*
 * btell - バッファード入力のtell()もどき
 */
long
btell(bp)
BFILE	*bp;
{
    if (bp->p == NULL)
	return (long)ERR;
    return bp->pos + (bp->p - bp->buf);
}

/*
 * bseek - バッファード入力のlseek()もどき
 */
int
bseek(bp, pos)
BFILE	*bp;
long	pos;
{
    int		n;

    if (bp->p
	&& pos >= bp->pos
	&& pos < bp->pos + (bp->ep - bp->buf)) {
	bp->p = bp->buf + (pos - bp->pos);
	return OK;
    }
    if (pos >= bp->size
	|| lseek(bp->fd, pos, SEEK_SET) < 0
	|| (n = read(bp->fd, bp->buf, BBUFSIZ)) <= 0) {
	bp->pos = 0L;
	bp->p = bp->ep = NULL;
	return ERR;
    }
    bp->pos = pos;
    bp->p = bp->buf;
    bp->ep = bp->buf + n;
    return OK;
}

/*
 * bread - バッファード入力のread()もどき
 */
int
bread(bp, buf, len)
BFILE	*bp;
uchr	*buf;
int	len;
{
    int		n;
    uchr	*p;

    if (bp->p == NULL)
	return ERR;
    p = buf;
    while (len > 0) {
	n = bp->ep - bp->p;
	if (n >= len) {
	    memcpy(p, bp->p, (size_t)len);
	    p += len;
	    bp->p += len;
	    len = 0;
	} else if (n > 0) {
	    memcpy(p, bp->p, (size_t)n);
		p += n;
		bp->p += n;
	    len -= n;
	} else {
	    n = read(bp->fd, bp->buf, BBUFSIZ);
	    if (n <= 0) {
		bp->pos = 0L;
		bp->p = bp->ep = NULL;
		break;
	    }
	    bp->pos += bp->ep - bp->buf;
	    bp->p = bp->buf;
	    bp->ep = bp->buf + n;
	}
    }
    return p - buf;
}

/*
 * bgetc - バッファード入力のgetc()もどき
 */
int
bgetc(bp)
BFILE	*bp;
{
    int		n;

    if (bp->p == NULL)
	return EOF;
    if (bp->p < bp->ep)
	return (int)*(bp->p++);
    n = read(bp->fd, bp->buf, BBUFSIZ);
    if (n <= 0) {
	bp->pos = 0L;
	bp->p = bp->ep = NULL;
	return EOF;
    }
    bp->pos += bp->ep - bp->buf;
    bp->p = bp->buf;
    bp->ep = bp->buf + n;
    return (int)*(bp->p++);
}

/*
 * bgetcbw - 現在の位置からファイル先頭方向にbgetc()する
 */
int
bgetcbw(bp)
BFILE	*bp;
{
    int		n;
    long	npos;

    if (bp->p == NULL)
	return EOF;
    if (bp->p > bp->buf)
	return (int)*(--bp->p);
    n = BBUFSIZ;
    npos = bp->pos - n;
    if (bp->pos < 0L) {
	npos = 0L;
	n = bp->pos - npos;
	if (n == 0)
	    return EOF;
    }
    if (lseek(bp->fd, npos, SEEK_SET) < 0
	|| read(bp->fd, bp->buf, n) < n) {
	bp->pos = 0L;
	bp->p = bp->ep = NULL;
	return EOF;
    }
    bp->pos = npos;
    bp->ep = bp->buf + n;
    bp->p = bp->ep;
    return (int)*(--bp->p);
}

/*
 * bclose - バッファード入力のclose()もどき
 */
int
bclose(bp)
BFILE	*bp;
{
    int		fd;

    fd = bp->fd;
    free((char *)bp);
    return close(fd);
}

/* -------------------- 日本語処理 -------------------- */

#define	KC_JIS		0
#define	KC_EUC		1
#define	KC_SJIS		2

#ifdef UNIX
#define	KC_DEF		KC_EUC
#endif
#ifdef MSDOS
#define	KC_DEF		KC_SJIS
#endif

/*
 * 外字表
 */
uchr	*gaijitbl[8][256] = {
    { /* G0 */
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"/",
	0,0,0,0,0,0,0,0,0,0,":",0,0,"=",0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,"...",0,0,0,0,0,0,0,0,0,0,
	0,0,0,"\"","\"",0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,"(C)",0,0,0,0,"(R)",0,
	0,0,0,0,0,0,0,"/",0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    }, { /* G1 */
    	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,"(タブー)",0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    }, { /* G2 */
    	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,"▼",0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    }, { /* G3 */
    	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,"(P)",0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,"←→",0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    }, { /* G4 */
    	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,"→",0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"《",
	"》","〔","〕",0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    }, { /* G5 */
    	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    }, { /* G6 */
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    }, { /* G99 */
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,"#",0,0,0,0,0,0,0,0,0,0,0,"/",
	0,0,0,0,0,0,0,0,0,0,0,0,"<",0,">",0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,"...",0,0,0,0,0,0,0,0,0,0,
	0,0,0,"\"","\"",0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	"A","A",0,0,0,0,0,0,"E","E",0,0,"I","I",0,0,
	0,0,"O","O",0,0,0,0,0,"U","U",0,0,0,0,0,
	"a","a",0,0,0,0,0,0,"e","e",0,0,"i","i",0,0,
	0,0,"o","o",0,0,0,0,0,"u","u",0,0,"y",0,0
    }
};

/*
 * デフォルト外字
 */
uchr	*gdeftbl[8] = { 0, 0, 0, 0, 0, 0 ,"〓", 0 };

typedef	struct _alttbl_t {
    uchr	org[4];			/* 元表記			*/
    uchr	*alt;			/* 代替表記			*/
} ALTTBL;

#define	ATBLINC		10		/* 代替表記表の増分		*/

uchr	have_alt[256];			/* その文字は代替表記を持つか?	*/
ALTTBL	*alttbl = NULL;			/* 代替表記表			*/
int	altnum = 0;			/* 代替表記表要素数		*/
int	in_alt = FALSE;			/* 代替表記中状態か?		*/

int	curpos = 0;			/* 現在の表示位置		*/
int	kcode = KC_DEF;			/* 入出力漢字コード		*/
int	jiskanji = 'B';			/* JIS漢字コードの漢字選択	*/
int	jisalpha = 'B';			/* JIS漢字コードの英字選択	*/
int	width = 0;			/* 出力行幅			*/
int	indent = 0;			/* インデント量			*/
int	kmode = FALSE;			/* 端末がJIS漢字指示状態か?	*/
int	rawmode = FALSE;		/* 出力無加工モード		*/
int	gfont = -1;			/* 外字フォント番号		*/
int	hlink = FALSE;			/* ハイパーリンク指定内		*/

int	add_alt();
int	init_alt();
uchr	*get_gaiji();
int	load_gaiji();
uchr	*tosjis();
void	outchar();
void	outstr();
void	set_indent();
void	newline();

/*
 * add_alt - 代替表記表に代替表記を追加する
 */
int
add_alt(org, alt)
uchr	*org, *alt;
{
    int		i;
    uchr	*p;
    static int	atblsiz = 0;

    for (i = 0; i < altnum; i++) {
	if (!strcmp(alttbl[i].org, org)) {
	    p = strpool(alt);
	    if (p == NULL)
		return ERR;
	    alttbl[i].alt = p;
	    return OK;
	}
    }
    if (alttbl == NULL) {
	atblsiz = ATBLINC;
	alttbl = (ALTTBL *)malloc(sizeof(ALTTBL) * atblsiz);
	altnum = 0;
    } else if (altnum >= atblsiz) {
	atblsiz += ATBLINC;
	alttbl = (ALTTBL *)realloc(alttbl, sizeof(ALTTBL) * atblsiz);
    }
    if (alttbl == NULL)
	return ERR;
    p = strpool(alt);
    if (p == NULL)
	return ERR;
    strcpy(alttbl[altnum].org, org);
    alttbl[altnum].alt = p;
    have_alt[*org] = TRUE;
    altnum++;
    return OK;
}

/*
 * init_alt - 代替表記表にデフォルト代替表記を登録する
 */
int
init_alt()
{
    int		err;

    err = 0;
    if (add_alt("\xa5", "/") == ERR) /* 1バイトカナの中点 */
	err++;
    return err;
}

/*
 * get_gaiji - フォント番号とコードから外字文字列を返す
 */
uchr *
get_gaiji(g, ch)
int	g, ch;
{
    uchr	*p;

    if (g == 9)
	g = 7;
    p = gaijitbl[g][ch];
    if (p == NULL)
	p = gdeftbl[g];
    return p;
}

/*
 * load_gaiji - 外字代替表記ファイルを読んで外字を登録する
 */
int
load_gaiji(file)
uchr	*file;
{
    int		c, ch, g, isalt;
    int		line, err;
    uchr	*p, *q;
    FILE	*fp;
    uchr	org[4], buf[256], tmp[256];

    if ((fp = fopen(file, "r")) == NULL) {
	outstr("ERR: 外字代替表記ファイルがオープンできません\n");
	return ERR;
    }
    line = 0;
    err = 0;
    while (fgets(buf, 256, fp) != NULL) {
	line++;
	buf[strlen(buf) - 1] = '\0';
	p = skipsp(buf);
	if (*p == '\0' || *p == ';')
	    continue;
	if (*p == '"') {	/* 代替表記 */
	    p++;
	    if (*p == '\\') {
		p = escchar(p, &c);
		org[0] = c;
		org[1] = '\0';
	    } else if (SJIS1(*p)) {
		org[0] = *p++;
		org[1] = *p++;
		org[2] = '\0';
	    } else {
		org[0] = *p++;
		org[1] = '\0';
	    }
	    if (*p != '"') {
		sprintf(tmp,
		    "ERR: line %d: 代替元文字が正しくありません\n", line);
		outstr(tmp);
		err++;
		continue;
	    }
	    p++;
	    isalt = TRUE;
	} else {	/* 外字定義 */
	    if (*p != 'G' || !strchr("01234569", p[1]) || p[2] != '-' ||
		!(isxdigit(p[3]) && isxdigit(p[4]) || p[3] == '*')) {
		sprintf(tmp,
		    "ERR: line %d: 外字番号が正しくありません\n", line);
		outstr(tmp);
		err++;
		continue;
	    }
	    g = p[1] - '0';
	    if (g == 9)
		g = 7;
	    if (p[3] == '*') {
		ch = -1;
		p += 4;
	    } else {
		ch = hexval(p + 3);
		p += 5;
	    }
	    isalt = FALSE;
	}
	p = skipsp(p);
	if (*p == '=')
	    p++;
	p = skipsp(p);
	if (*p != '"') {
	    sprintf(tmp,
		"ERR: line %d: 代替表記文字列が正しくありません\n", line);
	    outstr(tmp);
	    err++;
	    continue;
	}
	p++;
	q = tmp;
	while (*p && *p != '"') {
	    if (*p == '\\') {
		p = escchar(p, &c);
		if (c == '\\' || c == '<' || c == '>')
		    *q++ = '\\';
		*q++ = c;
		continue;
	    }
	    if (SJIS1(*p)) {
		*q++ = *p++;
		*q++ = *p++;
		continue;
	    }
	    if (*p == '<' || *p == '>' || *p == '#')
		*q++ = '\\';
	    *q++ = *p++;
	}
	if (*p != '"') {
	    sprintf(tmp,
		"ERR: line %d: 代替表記文字列が正しくありません\n", line);
	    outstr(tmp);
	    err++;
	    continue;
	}
	*q = '\0';
	if (isalt) {
	    if (add_alt(org, tmp) == ERR) {
		outstr("ERR: メモリーが足りません\n");
		err++;
	    }
	    continue;
	}
	if ((q = strpool(tmp)) == NULL) {
	    outstr("ERR: メモリーが足りません\n");
	    err++;
	    continue;
	}
	if (ch >= 0)
	    gaijitbl[g][ch] = q;
	else
	    gdeftbl[g] = q;
    }
    fclose(fp);
    if (err)
	return ERR;
    return OK;
}

#ifdef MSDOS
uchr *
tosjis(str)
uchr	*str;
{
    return str;
}

void
outchar(c)
int	c;
{
    if (c == 0)
	return;
    putchar(c);
}
#endif

#ifdef UNIX
/*
 * tosjis - kcodeで表現された文字列をシフトJISに変換する
 *	    (コマンド行引数の変換に使う)
 */
uchr *
tosjis(str)
uchr	*str;
{
    int		c, c1;
    int		c2, km;
    uchr	*p;
    static uchr	buf[128];

    if (kcode == KC_SJIS) {
	strcpy((char *)buf, str);
	return buf;
    }
    km = FALSE;
    p = buf;
    c1 = 0;
    while ((c = *str++) != '\0') {
	if (c1 != 0) {
	    c1 &= 0x7f;
	    c &= 0x7f;
	    if ((c1 & 0x01) == 0) {
		c += 0x7e;
	    } else {
		c += 0x1f;
		if (c > 0x7e)
		    c++;
	    }
	    c1 = (c1 + 0xe1) >> 1;
	    if (c1 > 0x9f)
		c1 += 0x40;
	    *p++ = c1;
	    *p++ = c;
	    c1 = 0;
	    continue;
	}
	if (c == '\033') {
	    c1 = *str++;
	    c2 = *str++;
	    if (c1 == '$' && (c2 == '@' || c2 == 'B'))
		km = TRUE;
	    else if (c1 == '(' && (c2 == 'J' || c2 == 'B'))
		km = FALSE;
	    continue;
	}
	if ((c & 0x80) || km == 1) {
	    c1 = c;
	    continue;
	}
	*p++ = c;
    }
    *p = '\0';
    return buf;
}

/*
 * outchar - シフトJISの文字を状態遷移を管理しながら
 *	     kcodeに変換して出力する
 *	     引数が0なら出力側の状態を英字に戻す(JISのとき必要)
 */
void
outchar(c)
int	c;
{
    static int	knj1 = 0;

    if (c == '\0') {
	if (kmode) {
	    printf("\033(%c", jisalpha);
	    kmode = FALSE;
	}
	return;
    }
    if (knj1 != 0) {
	if (kcode == KC_JIS && !kmode) {
	    printf("\033$%c", jiskanji);
	    kmode = TRUE;
	}
	if (kcode == KC_JIS || kcode == KC_EUC) {
	    if (knj1 > 0x9f)
		knj1 -= 0x40;
	    knj1 += knj1;
	    if (c <= 0x9e) {
		knj1 -= 0xe1;
		if (c >= 0x80)
		    c -= 1;
		c -= 0x1f;
	    } else {
		knj1 -= 0xe0;
		c -= 0x7e;
	    }
	    if (kcode == KC_EUC) {
		knj1 += 0x80;
		c += 0x80;
	    }
	}
	putchar(knj1);
	putchar(c);
	knj1 = 0;
	return;
    }
    if (c >= 0x81) {
	knj1 = c;
	return;
    }
    if (kmode) {
	printf("\033(%c", jisalpha);
	kmode = FALSE;
    }
    putchar(c);
}
#endif

/*
 * outstr - 外字を変換しながらoutchar経由で文字を出力する
 */
void
outstr(p)
uchr	*p;
{
    int		i, g;
    uchr	*gstr;
    uchr	tmp[16];

    while (*p) {
	if (rawmode) {
	    outchar(*p++);
	    continue;
	}
	if (*p == '<') {
	    if (p[1] == '/') {
		if (p[2] == 'G' && isdigit(p[3]))
		    gfont = -1;
		else if (!strncmp(p+2, "RUBY>", 5))
		    outstr(")");
		else if (!strncmp(p+2, "HL>", 3))
		    hlink = FALSE;
	    } else {
		if (p[1] == 'G')
		    gfont = p[2] - '0';
		else if (!strncmp(p+1, "RUBY>", 5))
		    outstr("(");
		else if (!strncmp(p+1, "HL>", 3))
		    hlink = TRUE;
	    }
	    while (*p != '>')
		p++;
	    p++;
	    continue;
	}
	if (*p == ':' && hlink) {
	    /*
	     * ハイパーリンク指定内の「:」以降はすべて無視
	     */
	    p++;
	    while (*p && (*p != '<' || strncmp(p, "</HL>", 5))) {
		if (SJIS1(*p))
		    p++;
		p++;
	    }
	    p += 5;
	    hlink = FALSE;
	    continue;
	}
	if (*p == '#' && p[3] == '#') {
	    g = (gfont == -1)? 0: gfont;
	    gstr = get_gaiji(g, hexval(p+1));
	    if (gstr) {
		outstr(gstr);
	    } else {
		sprintf(tmp, "\\<G%d-%c%c\\>", g, p[1], p[2]);
		outstr(tmp);
	    }
	    p += 4;
	    continue;
	}
	if (!in_alt && have_alt[*p]) {
	    for (i = 0; i < altnum; i++) {
		if (*p == alttbl[i].org[0] &&
		    (!SJIS1(*p) || p[1] == alttbl[i].org[1]))
		    break;
	    }
	    if (i < altnum) {
		in_alt = TRUE;
		outstr(alttbl[i].alt);
		in_alt = FALSE;
		if (SJIS1(*p))
		    p++;
		p++;
		continue;
	    }
	}
	if (*p == '\\')
	    p++;
	if (SJIS1(*p)) {
	    if (width > 0 && curpos >= width - 1)
		newline();
	    outchar(*p++);
	    outchar(*p++);
	    curpos += 2;
	    continue;
	}
	if (*p < ' ' || *p == 0x7f) {
	    switch (*p) {
	    case '\n':
		newline();
		p++;
		break;
	    case '\t':
		if (width > 0 && ((curpos | 0x07) + 1) > width)
		    newline();
		outchar(*p++);
		curpos = (curpos | 0x07) + 1;
		break;
	    default:
		outchar(*p++);
		break;
	    }
	    continue;
	}
	if (width > 0 && curpos >= width && !strchr("!),.:;?]}", *p))
	    newline();
	outchar(*p++);
	curpos++;
    }
    outchar(0);
}

/*
 * set_indent - インデント量を設定する
 */
void
set_indent(n)
int	n;
{
    indent = n;
}

/*
 * newline - 改行して設定量だけインデントする
 */
void
newline()
{
    outchar('\n');
    for (curpos = 0; curpos < indent; curpos++)
	outchar(' ');
}

/* -------------------- 表示書式処理 -------------------- */

/*
 * 本文データ行頭のラベル
 */
#define	LBL_ERROR	(-1)
#define	LBL_CMT		 0		/* 注釈(非表示)			*/
#define	LBL_DR		 1		/* 派生形			*/
#define	LBL_EN		 2		/* 見出し語			*/
#define	LBL_ENN		 3		/* 検索用異表記???(非表示)	*/
#define	LBL_ENV		 4		/* 異表記			*/
#define	LBL_ENVN	 5		/* 別綴り異表記???		*/
#define	LBL_ET		 6		/* 初出???			*/
#define	LBL_EV		 7		/* 同義語			*/
#define	LBL_EX		 8		/* 用例				*/
#define	LBL_EXKEY	 9		/* 用例検索キー			*/
#define	LBL_EXSUB	10		/* 用例サブ項目			*/
#define	LBL_F		11		/* IFの誤記???(tallith)		*/
#define	LBL_ID		12		/* 成句				*/
#define	LBL_IDSUB	13		/* 成句サブ項目			*/
#define	LBL_IF		14		/* 不規則活用			*/
#define	LBL_IMG		15		/* 図版データ(非表示)		*/
#define	LBL_J2E		16		/* 日本語から借用(adsuki bean)	*/
#define	LBL_KEY		17		/* 検索キー(非表示)		*/
#define	LBL_KEYEX	18		/* 用例検索キー(非表示)		*/
#define	LBL_KEYID	19		/* 成句検索キー(非表示)		*/
#define	LBL_LOC		20		/* 本文格納位置(非表示)		*/
#define	LBL_MEN		21		/* MNの誤記???			*/
#define	LBL_MN		22		/* 意味				*/
#define	LBL_MNSUB	23		/* 意味サブ項目			*/
#define	LBL_NB		24		/* 註				*/
#define	LBL_NBP		25		/* 表現註			*/
#define	LBL_NBPSUB	26		/* 表現註サブ項目		*/
#define	LBL_NBSUB	27		/* 註サブ項目			*/
#define	LBL_NSBUS	28		/* NBSUBの誤記???(and)		*/
#define	LBL_OT		29		/* 類語、発音、...		*/
#define	LBL_OTSUB	30		/* 類語サブ項目			*/
#define	LBL_PR		31		/* 発音				*/
#define	LBL_PS		32		/* 品詞				*/
#define	LBL_RA		33		/* ???(非表示)			*/
#define	LBL_RES		34		/* ???(非表示)			*/
#define	LBL_RN		35		/* ???(非表示)			*/
#define	LBL_SM		36		/* ???(非表示)			*/
#define	LBL_SND		37		/* 音声データ(非表示)		*/
#define	LBL_SRC		38		/* 語源				*/
#define	LBL_SRCSUB	39		/* 語源サブ項目			*/
#define	LBL_TL		40		/* 文献タイトル			*/
#define	LBL_TLSUB	41		/* 文献タイトルサブ項目		*/
#define	LBL_TQ		42		/* 意味分類(getの「I 所有する」)*/
#define	LBL_TR		43		/* 語意				*/
#define	LBL_TRSUB	44		/* 語意サブ項目			*/
/*
 * 以下はcsrd内部処理用
 */
#define	LBL_ZEX		45		/* 成句・用例検索結果の用例	*/
#define	LBL_ZID		46		/* 成句・用例検索結果の成句	*/

typedef	struct _lbltbl_t {
    int		id;			/* ラベル識別ID			*/
    int		disp;			/* 出力するか?			*/
    uchr	*label;			/* ラベル文字列			*/
    uchr	*leader;		/* 先頭に出力する文字列		*/
} LBLTBL;

/*
 * ラベル表
 */
LBLTBL	labeltbl[] = {
    { LBL_CMT,		FALSE,	"CMT",		""	},
    { LBL_DR,		TRUE,	"DR",		""	},
    { LBL_EN,		TRUE,	"EN",		"□ "	},
    { LBL_ENN,		FALSE,	"ENN",		""	},
    { LBL_ENV,		TRUE,	"ENV",		""	},
    { LBL_ENVN,		TRUE,	"ENVN",		""	},
    { LBL_ET,		TRUE,	"ET",		""	},
    { LBL_EV,		TRUE,	"EV",		""	},
    { LBL_EX,		TRUE,	"EX",		"  "	},
    { LBL_EXKEY,	FALSE,	"EXKEY",	""	},
    { LBL_EXSUB,	TRUE,	"EXSUB",	""	},
    { LBL_F,		TRUE,	"F",		""	},
    { LBL_ID,		TRUE,	"ID",		""	},
    { LBL_IDSUB,	TRUE,	"IDSUB",	""	},
    { LBL_IF,		TRUE,	"IF",		""	},
    { LBL_IMG,		FALSE,	"IMG",		""	},
    { LBL_J2E,		TRUE,	"J2E",		""	},
    { LBL_KEY,		FALSE,	"KEY",		""	},
    { LBL_KEYEX,	FALSE,	"KEYEX",	""	},
    { LBL_KEYID,	FALSE,	"KEYID",	""	},
    { LBL_LOC,		FALSE,	"LOC",		""	},
    { LBL_MEN,		TRUE,	"MEN",		""	},
    { LBL_MN,		TRUE,	"MN",		""	},
    { LBL_MNSUB,	TRUE,	"MNSUB",	""	},
    { LBL_NB,		TRUE,	"NB",		""	},
    { LBL_NBP,		TRUE,	"NBP",		"  "	},
    { LBL_NBPSUB,	TRUE,	"NBPSUB",	""	},
    { LBL_NBSUB,	TRUE,	"NBSUB",	""	},
    { LBL_NSBUS,	TRUE,	"NSBUS",	""	},
    { LBL_OT,		TRUE,	"OT",		""	},
    { LBL_OTSUB,	TRUE,	"OTSUB",	""	},
    { LBL_PR,		TRUE,	"PR",		" "	},
    { LBL_PS,		TRUE,	"PS",		"\n■"	},
    { LBL_RA,		FALSE,	"RA",		""	},
    { LBL_RES,		FALSE,	"RES",		""	},
    { LBL_RN,		FALSE,	"RN",		""	},
    { LBL_SM,		TRUE,	"SM",		""	},
    { LBL_SND,		FALSE,	"SND",		""	},
    { LBL_SRC,		TRUE,	"SRC",		""	},
    { LBL_SRCSUB,	TRUE,	"SRCSUB",	""	},
    { LBL_TL,		TRUE,	"TL",		""	},
    { LBL_TLSUB,	TRUE,	"TLSUB",	""	},
    { LBL_TQ,		TRUE,	"TQ",		"\n"	},
    { LBL_TR,		TRUE,	"TR",		""	},
    { LBL_TRSUB,	TRUE,	"TRSUB",	""	},
    { LBL_ZEX,		TRUE,	"ZEX",		""	},
    { LBL_ZID,		TRUE,	"ZID",		""	},
    { LBL_ERROR,	FALSE,	NULL,		NULL	}
};

LBLTBL	*get_label();
int	load_format();
int	suppress();

/*
 * get_label - 行頭のラベルを識別する
 */
LBLTBL *
get_label(str)
uchr	*str;
{
    uchr	*p, *q;
    LBLTBL	*lp;
    uchr	tmp[128];

    p = str;
    q = tmp;
    while (*p && isalpha(*p) && isupper(*p))
	*q++ = *p++;
    *q = '\0';
    if (!strncmp(str, "J2E", 3))
	strcpy(tmp, "J2E");
    lp = labeltbl;
    while (lp->label != NULL) {
	if (!strncmp(tmp, lp->label, strlen(tmp)))
	    break;
	lp++;
    }
    if (lp->id == LBL_OT) {
	if (isdigit(str[2]) && str[3] == 'S')
	    lp++;	/* OTの代わりにOTSUBを指すようにする */
    }
    return lp;
}

/*
 * load_format - 書式ファイルを読んでラベル表を更新する
 */
int
load_format(file)
uchr	*file;
{
    int		c, line, err, disp;
    uchr	*p, *q;
    FILE	*fp;
    LBLTBL	*lp;
    uchr	buf[256], tmp[256];

    if ((fp = fopen(file, "r")) == NULL) {
	outstr("ERR: 表示書式ファイルがオープンできません\n");
	return ERR;
    }
    line = 0;
    err = 0;
    while (fgets(buf, 256, fp) != NULL) {
	line++;
	buf[strlen(buf) - 1] = '\0';
	disp = TRUE;
	p = skipsp(buf);
	if (*p == '\0' || *p == ';')
	    continue;
	if (*p == '!') {
	    disp = FALSE;
	    p++;
	}
	p = skipsp(p);
	lp = get_label(p);
	if (lp->id == LBL_ERROR) {
	    sprintf(tmp,
		"ERR: line %d: 書式項目名が正しくありません\n", line);
	    outstr(tmp);
	    err++;
	    continue;
	}
	while (*p && (isupper(*p) || isdigit(*p)))
	    p++;
	p = skipsp(p);
	if (*p == '=')
	    p++;
	p = skipsp(p);
	if (*p != '"') {
	    sprintf(tmp,
		"ERR: line %d: 書式文字列が正しくありません\n", line);
	    outstr(tmp);
	    err++;
	    continue;
	}
	p++;
	q = tmp;
	while (*p && *p != '"') {
	    if (*p == '\\') {
		p = escchar(p, &c);
		if (c == '\\' || c == '<' || c == '>')
		    *q++ = '\\';
		*q++ = c;
		continue;
	    }
	    if (SJIS1(*p)) {
		*q++ = *p++;
		*q++ = *p++;
		continue;
	    }
	    if (*p == '<' || *p == '>' || *p == '#')
		*q++ = '\\';
	    *q++ = *p++;
	}
	if (*p != '"') {
	    sprintf(tmp,
		"ERR: line %d: 書式文字列が正しくありません\n", line);
	    outstr(tmp);
	    err++;
	    continue;
	}
	*q = '\0';
	if ((q = strpool(tmp)) == NULL) {
	    outstr("ERR: メモリーが足りません\n");
	    err++;
	    continue;
	}
	lp->leader = q;
	lp->disp = disp;
    }
    fclose(fp);
    if (err)
	return ERR;
    return OK;
}

/*
 * suppress - 文字列で指定された特定項目の表示を抑制する
 */
int
suppress(str)
uchr	*str;
{
    if (str == NULL)
	return OK;
    if (!strcmp(str, "a"))
	str = "teio";
    while (*str) {
	switch (*str) {
	case 't':
	    get_label("TR")->disp = FALSE;
	    get_label("TRSUB")->disp = FALSE;
	    break;
	case 'e':
	    get_label("EX")->disp = FALSE;
	    get_label("EXSUB")->disp = FALSE;
	    get_label("ZEX")->disp = FALSE;
	    break;
	case 'i':
	    get_label("ID")->disp = FALSE;
	    get_label("IDSUB")->disp = FALSE;
	    get_label("ZID")->disp = FALSE;
	    break;
	case 'o':
	    get_label("OT")->disp = FALSE;
	    get_label("OTSUB")->disp = FALSE;
	    break;
	default:
	    return ERR;
	}
	str++;
    }
    return OK;
}

/* -------------------- 本文表示 -------------------- */

#define	MAIN_DAT	"main.txt"	/* 本文				*/

#define	REV_LF		0xf5		/* 0x0a(LF)のビット反転		*/

#define	LBUFSIZ		4096		/* 行バッファーのサイズ		*/
					/* 現バージョンのmain.datの最大	*/
					/* 行長は2389だが余裕をみておく	*/

/*
 * 発音記号列は「{;」と「2}」で囲まれる(VEGA対応仕様)
 */
#define	BP_LEADER	'{'		/* csrd側点字発音記号列開始	*/
#define	BP_START	';'		/* 点字発音記号列開始		*/
#define	BP_END		'2'		/* 点字発音記号列終了		*/
#define	BP_TRAILER	'}'		/* csrd側点字発音記号列終了	*/

/*
 * BPTBL.gcのマスク
 */
#define	CHMASK		0x00ff		/* 文字コード			*/
#define	GRMASK		0x1f00		/* フォントグループ		*/
#define	ITMASK		0x8000		/* イタリック			*/

#define	GRNMASK		0x0f00		/* フォントグループ番号		*/
#define	GRUNDEF		0x1000		/* フォントグループ未定義	*/

#define	GNUM(g)		(((g) == 0x0900)? 99: ((g) >> 8))

typedef	struct _bptbl_t {
    unt		gc;			/* フォントグループ+文字コード	*/
    uchr	bps[4];			/* 点字発音記号列		*/
} BPTBL;

/*
 * 点字発音記号列表
 */
BPTBL bptbl[] = {
    { 'a',	"A"	},		/* a				*/
    { 'b',	"B"	},		/* b				*/
    { 'd',	"D"	},		/* d				*/
    { 'e',	"E"	},		/* e				*/
    { 'f',	"F"	},		/* f				*/
    { 'g',	"G"	},		/* g				*/
    { 'h',	"H"	},		/* h				*/
    { 'i',	"I"	},		/* i				*/
    { 'j',	"J"	},		/* j				*/
    { 'k',	"K"	},		/* k				*/
    { 'l',	"L"	},		/* l				*/
    { 'm',	"M"	},		/* m				*/
    { 'n',	"N"	},		/* n				*/
    { 'o',	"O"	},		/* o				*/
    { 'p',	"P"	},		/* p				*/
    { 'r',	"R"	},		/* r				*/
    { 's',	"S"	},		/* s				*/
    { 't',	"T"	},		/* t				*/
    { 'u',	"U"	},		/* u				*/
    { 'v',	"V"	},		/* v				*/
    { 'w',	"W"	},		/* w				*/
    { 'z',	"Z"	},		/* z				*/
    { 0x00e0,	"^A"	},		/* a`				*/
    { 0x00e1,	"_A"	},		/* a´				*/
    { 0x00e6,	"%"	},		/* aeの合字			*/
    { 0x00e8,	"^E"	},		/* e`				*/
    { 0x00e9,	"_E"	},		/* e´				*/
    { 0x00ec,	"^I"	},		/* i`				*/
    { 0x00ed,	"_I"	},		/* i´				*/
    { 0x00f0,	"\\]"	},		/* theのth			*/
    { 0x00f2,	"^O"	},		/* o`				*/
    { 0x00f3,	"_O"	},		/* o´				*/
    { 0x00f9,	"^U"	},		/* u`				*/
    { 0x00fa,	"_U"	},		/* u´				*/
    { 0x0141,	"*"	},		/* とさかのないa		*/
    { 0x0142,	"\\<"	},		/* 左の開いたo			*/
    { 0x0143,	"_+"	},		/* 横棒のないAに´		*/
    { 0x014a,	"_%"	},		/* aeの合字に´			*/
    { 0x014b,	"^%"	},		/* aeの合字に`			*/
    { 0x014c,	"_5"	},		/* あいまい母音に´		*/
    { 0x014d,	"^5"	},		/* あいまい母音に`		*/
    { 0x014e,	"_*"	},		/* とさかのないaに´		*/
    { 0x014f,	"^*"	},		/* とさかのないaに`		*/
    { 0x0150,	":"	},		/* sheのsh			*/
    { 0x0151,	"_\\<"	},		/* 左の開いたoに´		*/
    { 0x0152,	"^\\<"	},		/* 左の開いたoに`		*/
    { 0x0153,	"_\\>"	},		/* 丸いEに´			*/
    { 0x0154,	"^\\>"	},		/* 丸いEに`			*/
    { 0x016a,	"?"	},		/* throughのth			*/
    { 0x0173,	"!"	},		/* 柔らかいg			*/
    { 0x0176,	"$"	},		/* singのng			*/
    { 0x02e1,	"^+"	},		/* 横棒のないAに`		*/
    { 0x03b9,	"5"	},		/* あいまい母音			*/
    { 0x0477,	"3"	}		/* 伸ばす音			*/
};

#define	NBPS	(sizeof(bptbl) 	/ sizeof(bptbl[0]))

int	do_braille = FALSE;		/* 点字発音記号変換を行うか?	*/
int	nobracket = FALSE;		/* 元の発音囲み[]を削除するか?	*/
int	firstidiom;			/* その品詞の最初の成句		*/
int	have_en;			/* 見出し単語表示直後(無改行時)	*/
uchr	line[LBUFSIZ];			/* 1行データ表示用作業領域	*/

uchr	*bpstr();
uchr	*bp_transfer();
BFILE	*dic_open();
int	dgetline();
int	showline();
int	show_data();
int	show_idiom();
int	show_header();
int	show_version();

/*
 * bpstr - 文字に対応する点字発音記号列を返す
 */
uchr *
bpstr(gc)
unt	gc;
{
    int		i;

    gc &= (GRNMASK|CHMASK);	/* GRUNDEFは<G0>と同じ */
    for (i = 0; i < NBPS; i++) {
	if (gc == bptbl[i].gc)
	    return bptbl[i].bps;
    }
    return NULL;
}

/*
 * bp_transfer - 発音記号列の点訳を試みる
 *		 結果は渡されたバッファーを上書きする形で書き込む
 *		 変換結果文字列ではタグの開閉が狂っている可能性あり
 */
uchr *
bp_transfer(line)
uchr	*line;
{
    int		level;
    unt		c, it, nit, g, ng;
    uchr	*p, *q;
    unt		*u, *uu, *upto;
    static unt	tmp[1024];	/* 変換元データは最大776バイト	*/

#ifdef DEBUG
    if (debug)
	printf("line in= \"%s\"\n", line);
#endif
    /*
     * tmpにフォント/イタリック情報付き文字列を組み立てる
     */
    g = GRUNDEF;
    it = 0;
    p = line;
    u = tmp;
    while (*p) {
	if (*p == '<') {
	    if (p[1] == 'G')
		g = (p[2] - '0') << 8;
	    else if (p[1] == '/' && p[2] == 'G')
		g = GRUNDEF;
	    else if (p[1] == 'I' && p[2] == '>')
		it = ITMASK;
	    else if (p[1] == '/' && p[2] == 'I' && p[3] == '>')
		it = 0;
	    else {
		while (*p != '>')
		    *u++ = it | g | *p++;
		*u++ = it | g | *p++;
		continue;
	    }
	    while (*p++ != '>')
		;
	    continue;
	}
	if (SJIS1(*p)) {
	    *u++ = it | g | *p++;
	    *u++ = it | g | *p++;
	    continue;
	}
	if (*p == '#' && p[3] == '#') {
	    *u++ = it | g | hexval(p + 1);
	    p += 4;
	    continue;
	}
	*u++ = it | g | *p++;
    }
    *u = '\0';
    /*
     * 発音記号列を解釈しながらlineを作り直す
     */
    g = GRUNDEF;
    it = 0;
    level = 0;
    p = line;
    u = tmp;
    upto = NULL;
    while (*u) {
	nit = *u & ITMASK;
	if (nit != it) {
	    strcpy(p, nit? "<I>": "</I>");
	    while (*p++ != '>')
		;
	    it = nit;
	}
	ng = *u & GRMASK;
	if (ng != g) {
	    if (g != GRUNDEF) {
		sprintf(p, "</G%d>", GNUM(g));
		while (*p++ != '>')
		    ;
	    }
	    if (ng != GRUNDEF) {
		sprintf(p, "<G%d>", GNUM(ng));
		while (*p++ != '>')
		    ;
	    }
	    g = ng;
	}
	if ((*u & GRUNDEF) != 0 && SJIS1(*u & CHMASK)) {
	    *p++ = (*u++ & CHMASK);
	    *p++ = (*u++ & CHMASK);
	    continue;
	}
	if (upto && u < upto)
	    goto notrans;
	switch (*u & (GRNMASK|CHMASK)) {
	case '[':
	    if (!nobracket)
		*p++ = *u & CHMASK;
	    level++;
	    break;
	case ']':
	    if (!nobracket)
		*p++ = *u & CHMASK;
	    if (level > 0)
		level--;
	    break;
	default:
	    if (level > 0 && bpstr(*u) != NULL) {
		uu = u;
		while (*uu && bpstr(*uu)) {
		    uu++;
		}
		c = *uu & (GRMASK|CHMASK);
		if (c == ' ' || c == ';' || c == ',' ||
		    c == '-' || c == ']' || c == '|') {
		    if (g != GRUNDEF) {
			sprintf(p, "</G%d>", GNUM(g));
			while (*p++ != '>')
			    ;
			g = GRUNDEF;
		    }
		    *p++ = BP_LEADER;
		    *p++ = BP_START;
		    while (u < uu) {
			q = bpstr(*u++);
			while (*q)
			    *p++ = *q++;
		    }
		    *p++ = BP_END;
		    *p++ = BP_TRAILER;
		    u = uu;
		    continue;
		}
		upto = uu;
	    }
	notrans:
	    ng = *u & GRMASK;
	    c = *u & CHMASK;
	    if (ng == GRUNDEF) {
		*p++ = c;
	    } else if (ng != 0 ||
		c >= 0x80 || c == '/' || c == ':' || c == '=') {
		sprintf(p, "#%02x#", c);
		p += 4;
	    } else {
		*p++ = c;
	    }
	}
	u++;
    }
    *p = '\0';
    if (it) {
	strcpy(p, "</I>");
	p += 4;
    }
    if (g != GRUNDEF)
	sprintf(p, "</G%d>", GNUM(g));
#ifdef DEBUG
    if (debug)
	printf("line out=\"%s\"\n", line);
#endif
    return line;
}

/*
 * dic_open - 辞書データファイルをbopen()する
 */
BFILE *
dic_open(dicdir, dicfile)
uchr	*dicdir, *dicfile;
{
    BFILE	*bfp;
    uchr	buf[256];

    sprintf(buf, "%s/%s", dicdir, dicfile);
    if ((bfp = bopen(buf)) == NULL) {
	outstr("ERR: 辞書ファイルがオープンできません(");
	outstr(buf);
	outstr(")\n");
	return NULL;
    }
    return bfp;
}

/*
 * dgetline - 本文データを1行読み込み、改行を除いた長さを返す
 */
int
dgetline(bfp, buf)
BFILE	*bfp;
uchr	*buf;
{
    int		c;
    uchr	*p;

    p = buf;
    while ((c = bgetc(bfp)) != REV_LF)
	*p++ = ~c;
    *p = '\0';
    return p - buf;
}

/*
 * showline - 1行の本文データを表示する
 */
int
showline(line)
uchr	*line;
{
    uchr	*p, *q;
    LBLTBL	*lp;

    if (rawmode) {
	outstr(line);
	outstr("\n");
	return OK;
    }
    lp = get_label(line);
    if (lp->id == LBL_ERROR) {
#ifdef DEBUG
	if (debug) {
	    outstr("ERR> ");
	    outstr(line);
	    outstr("\n");
	}
#endif
	return ERR;
    }
    if (!lp->disp) {
#ifdef DEBUG
	if (debug) {
	    outstr("[");
	    outstr(lp->label);
	    outstr("*]\n");
	}
#endif
	return OK;
    }
    if (have_en && lp->id != LBL_PR) {
	set_indent(0);
	outstr("\n");
    }
    have_en = FALSE;
#ifdef DEBUG
    if (debug) {
	outstr("[");
	outstr(lp->label);
	outstr("] ");
    }
#endif
    p = skipeq(line);
    if (*p == '\0')
	return OK;
    set_indent(nspaces(lp->leader));
    outstr(lp->leader);
    switch (lp->id) {
    case LBL_DR:
	q = p;
	while (*q && *q != '|')
	    q++;
	*q = 0;
	outstr(p);
	set_indent(0);
	outstr("\n");
	break;
    case LBL_EN:
	q = p;
	while (*q && *q != '|')
	    q++;
	*q = 0;
	outstr(p);
	have_en = TRUE;
	break;
    case LBL_ID:
	if (firstidiom) {
	    outstr("\n【成句】\n");
	    firstidiom = FALSE;
	}
	goto normal;
    case LBL_PS:
	firstidiom = TRUE;
	goto normal;
    case LBL_TRSUB:
	if (!strncmp(p, "【", 2)) {
	    p += 2;
	    while (*p && strncmp(p, "】", 2)) {
		if (SJIS1(*p))
		    p++;
		p++;
	    }
	    if (*p)
		p += 2;
	}
	goto normal;
    case LBL_PR:
    case LBL_IF:
	if (do_braille)
	    p = bp_transfer(p);
	goto normal;
    default:
    normal:
	outstr(p);
	set_indent(0);
	outstr("\n");
	break;
    }
    return OK;
}

/*
 * show_data - 本文データを表示する
 */
int
show_data(bfp, dpos, dlen)
BFILE	*bfp;
long	dpos, dlen;
{
    int		len;

    if (bseek(bfp, dpos) == ERR)
	return ERR;
    firstidiom = TRUE;
    while (dlen > 0L) {
	len = dgetline(bfp, line);
	showline(line);
	dlen -= (long)(len + 1);
    }
    return OK;
}

/*
 * show_idiom - 成句・用例の検索結果を表示する
 */
int
show_idiom(bfp, dpos)
BFILE	*bfp;
long	dpos;
{
    int		idiom;
    long	npos;
    uchr	*p, *q;
    LBLTBL	*lp;
    uchr	ps[128], tr[32];

    /*
     * あとで項目先頭まで遡る必要があるので
     * 成句・用例を読み出せるだけの余裕を残して
     * ファイル先頭寄りにいったんbseek()し
     * それから本来の位置にbseek()する
     * こうすることでread()の回数が減らせるはず
     */
    npos = dpos - (BBUFSIZ - 512);
    if (npos < 0L)
	npos = 0;
    if (bseek(bfp, npos) == ERR)
	return ERR;
    if (bseek(bfp, dpos) == ERR)
	return ERR;
    dgetline(bfp, line);
    if (rawmode) {
	outstr(line);
	outstr("\n");
	return OK;
    }
    if (*line == 'I' || *line == 'D') {
	lp = get_label("ZID");
	idiom = TRUE;
    } else {
	lp = get_label("ZEX");
	idiom = FALSE;
    }
    if (!lp->disp)
	return OK;
    set_indent(nspaces(lp->leader));
    outstr(lp->leader);
    outstr(skipeq(line));
    /*
     * ファイル先頭に遡ってEN=を探し、
     * そこから現在位置まで必要な情報を読んで表示する
     */
    if (bseek(bfp, dpos) == ERR)
	return ERR;
    for (;;) {
	if ((bgetcbw(bfp) ^ 0xff) == '=' &&
	    (bgetcbw(bfp) ^ 0xff) == 'N' &&
	    (bgetcbw(bfp) ^ 0xff) == 'E' &&
	    (bgetcbw(bfp) ^ 0xff) == '\n')
	    break;
    }
    bgetc(bfp);
    outstr(" (");
    dgetline(bfp, line);
    p = q = skipeq(line);
    while (*q && *q != '|')
	q++;
    *q = '\0';
    outstr(p);
    *ps = *tr = '\0';
    while (btell(bfp) < dpos) {
	dgetline(bfp, line);
	if (!strncmp(line, "PS=", 3)) {
	    strcpy(ps, line + 3);
	    *tr = '\0';
	    continue;
	}
	if (!idiom && !strncmp(line, "TR", 2)) {
	    p = line + 2;
	    if (*p == 'S') /* TRSUB */
		p += 3;
	    q = tr;
	    while (*p && *p != '=')
		*q++ = *p++;
	    *q = '\0';
	}
    }
    if (*ps) {
	outstr(", ");
	outstr(ps);
	if (*tr)
	    outstr(tr);
	if (idiom)
	    outstr(" 成句");
    }
    outstr(")");
    set_indent(0);
    outstr("\n");
    return OK;
}

/*
 * show_header - ENにある見出しデータを表示する
 */
int
show_header(bfp, dpos)
BFILE	*bfp;
long	dpos;
{
    uchr	*p, *q;

    if (bseek(bfp, dpos) == ERR)
	return ERR;
    for (;;) {
	if (dgetline(bfp, line) <= 0)
	    return ERR;
	if (get_label(line)->id != LBL_EN)
	    continue;
	p = skipeq(line);
	q = p;
	while (*q && *q != '|')
	    q++;
	*q = 0;
	outstr(p);
	break;
    }
    return OK;
}

/*
 * show_version - 辞書本文データのバージョンを表示する
 */
int
show_version(dicdir)
uchr	*dicdir;
{
    uchr	*p;
    BFILE	*bfp;

    if ((bfp = dic_open(dicdir, MAIN_DAT)) == NULL)
	return ERR;
    bseek(bfp, 0L);
    while (dgetline(bfp, line) > 0 && !strncmp(line, "CMT=", 4)) {
	if (rawmode) {
	    outstr(line);
	    outstr("\n");
	    continue;
	}
	p = line + 4;
	if (!strncmp(p, "***", 3) ||
	    !strncmp(p, "TITLE:", 6) ||
	    !strncmp(p, "Based", 5) ||
	    !strncmp(p, "DATE:", 5)) {
	    outstr(p);
	    outstr("\n");
	}
    }
    bclose(bfp);
    return OK;
}

/* --------------- 成句・用例検索作業ファイル管理 --------------- */

#define	TT0SIZ		1000		/* 検索結果格納配列の要素数	*/

/*
 * データ格納先
 */
#define	TF_USE0		0		/* 配列0だけを使う		*/
#define	TF_USE01	1		/* 配列0とファイル1を使う	*/
#define	TF_USE1		2		/* ファイル1だけを使う		*/
#define	TF_USE12	3		/* ファイル1とファイル2を使う	*/

typedef	struct _tftbl {
    long	val;			/* データ値			*/
    long	num;			/* データ数			*/
    int		n0;			/* tt0のデータ数		*/
    int		n0x;			/* tt0の用例開始位置/現在位置	*/
    long	n1;			/* tf1のデータ数		*/
    long	n1x;			/* tf1の現在位置		*/
    long	n2;			/* tf2のデータ数		*/
    long	n2x;			/* tf2の現在位置		*/
    long	*tt0;			/* 作業配列0			*/
    uchr	*tf1;			/* 作業ファイル1		*/
    uchr	*tf2;			/* 作業ファイル2		*/
    FILE	*fp1;			/* 作業ファイル1 FP		*/
    FILE	*fp2;			/* 作業ファイル2 FP		*/
    long	v1;			/* tf1の先読みデータ		*/
    long	v2;			/* tf2の先読みデータ		*/
    int		use;			/* データ格納先			*/
    int		open;			/* オープンされているか?	*/
} TFTBL;

TFTBL	*tftbl;				/* 作業ファイル管理表		*/

int	setuptmp();
void	cleantmp();
int	twopen();
int	twrite();
int	twclose();
int	tropen();
long	tread();
int	trclose();
int	tisopen();
void	tswap();
void	tfree();
int	lcmp();
long	getlval();

/*
 * setuptmp - 作業ファイル管理表を準備する
 */
int
setuptmp()
{
    int		n, len;
    uchr	*tmpdir;
    TFTBL	*tp;
    uchr	tmp[128];

    tftbl = (TFTBL *)malloc(sizeof(TFTBL) * 3);
    if (tftbl == NULL)
	return ERR;
    len = 0;
    if ((tmpdir = getenv("TMP")) != NULL ||
	(tmpdir = getenv("TEMP")) != NULL) {
	strcpy(tmp, tmpdir);
	bsltosl(tmp);
	len = strlen(tmp);
	if (tmp[len-1] != '/')
	    tmp[len++] = '/';
    }
    tp = tftbl;
    for (n = 0; n < 3; n++) {
	sprintf(tmp+len, "cs%d1XXXXXX", n);
	tp->tf1 = strpool(tmp);
	sprintf(tmp+len, "cs%d2XXXXXX", n);
	tp->tf2 = strpool(tmp);
	if (tp->tf1 == NULL || tp->tf2 == NULL)
	    return ERR;
	mktemp(tp->tf1);
	mktemp(tp->tf2);
	tp->fp1 = NULL;
	tp->fp2 = NULL;
	tp->open = FALSE;
	tp++;
    }
    tp = tftbl;
    for (n = 0; n < 3; n++) {
	tp->tt0 = (long *)malloc(TT0SIZ * sizeof(long));
	/*
	 * メモリーが不足ならtt0は使えなくてもよい
	 * エラーにはしない
	 */
	tp++;
    }
#ifdef DEBUG
    if (debug2) {
	tp = tftbl;
	for (n = 0; n < 3; n++) {
	    printf("tftbl[%d]:\n", n);
	    printf(" tt0=%s\n", tp->tt0? "(in use)": "(not in use)");
	    printf(" tf1=%s\n", tp->tf1);
	    printf(" tf2=%s\n", tp->tf2);
	    tp++;
	}
    }
#endif
    return OK;
}

/*
 * cleantmp - 残っているかもしれない作業ファイルを削除する
 */
void
cleantmp()
{
    int		n;
    TFTBL	*tp;

    if (tftbl == NULL)
	return;
    tp = tftbl;
    for (n = 0; n < 3; n++) {
	if (tp->fp1 != NULL) {
	    fclose(tp->fp1);
	    tp->fp1 = NULL;
	}
	if (tp->fp2 != NULL) {
	    fclose(tp->fp2);
	    tp->fp2 = NULL;
	}
	unlink(tp->tf1);
	unlink(tp->tf2);
	tp->open = FALSE;
	tp++;
    }
}

/*
 * twopen - 作業ファイルセットを書き出しオープンする
 */
int
twopen(tn)
int	tn;
{
    TFTBL	*tp;

    tp = &tftbl[tn];
    tp->val = 0L;
    tp->num = 0L;
    tp->n0 = tp->n0x = 0;
    tp->n1 = tp->n1x = 0L;
    tp->n2 = tp->n2x = 0L;
    tp->fp1 = tp->fp2 = NULL;
    tp->use = (tp->tt0 != NULL)? TF_USE0: TF_USE1;
    tp->open = TRUE;
    return OK;
}

/*
 * twrite - 作業ファイルセットに書き出す
 */
int
twrite(tn, val)
int	tn;
long	val;
{
    int		n;
    TFTBL	*tp;

    tp = &tftbl[tn];
    switch (tp->use) {
    case TF_USE0:
	if (tp->n0 >= TT0SIZ) {
	    /*
	     * 配列に収まらなかったのでtf1を使う
	     */
	    if ((tp->fp1 = fopen(tp->tf1, "w")) == NULL)
		goto openerr;
#ifdef DEBUG
	    if (debug2) {
		printf("twrite(%d): open tf1(%s), num=%ld, val=%07lx\n",
		    tn, tp->tf1, tp->num, val);
	    }
#endif
	    for (n = tp->n0x; n < TT0SIZ; n++) {
		if (fprintf(tp->fp1, "%07lx\n", tp->tt0[n]) == EOF)
		    goto writeerr;
	    }
	    tp->n0 = tp->n0x;
	    tp->n1 = (long)(TT0SIZ - tp->n0x);
	    if (tp->n0x > 0) {
		/*
		 * 配列中に成句と用例の境界があった
		 * 成句は残して用例だけtf1に移動された
		 * あとでtt0をソートする必要はない(n0x=0)
		 */
		tp->n0x = 0;
		tp->use = TF_USE01;
		goto use01;
	    }
	    /*
	     * 全体がtf1に移動された
	     */
	    tp->use = TF_USE1;
	    goto use1;
	}
	if (val < tp->val) {
	    /*
	     * 成句と用例の境界が来た
	     */
	    tp->n0x = tp->n0;
	}
	tp->tt0[tp->n0++] = val;
	break;
    case TF_USE01:
    use01:
	if (val < tp->val)
	    goto fmterr;
	goto use1write;
    case TF_USE1:
    use1:
	if (val < tp->val) {
	    /*
	     * 成句と用例の境界が来た
	     * 以後はtf2を使う
	     */
	    if ((tp->fp2 = fopen(tp->tf2, "w")) == NULL)
		goto openerr;
#ifdef DEBUG
	    if (debug2) {
		printf("twrite(%d): open tf2(%s), num=%ld, val=%07lx\n",
		    tn, tp->tf1, tp->num, val);
	    }
#endif
	    tp->use = TF_USE12;
	    goto use12;
	}
    use1write:
	if (fprintf(tp->fp1, "%07lx\n", val) == EOF)
	    goto writeerr;
	tp->n1++;
	break;
    case TF_USE12:
	if (val < tp->val)
	    goto fmterr;
    use12:
	if (fprintf(tp->fp2, "%07lx\n", val) == EOF)
	    return ERR;
	tp->n2++;
	break;
    }
    tp->val = val;
    tp->num++;
    return OK;
openerr:
    outstr("ERR: 作業用ファイルが新規作成できません\n");
    return ERR;
writeerr:
    outstr("ERR: ディスクがいっぱいです\n");
    return ERR;
fmterr:
    outstr("PANIC: 成句・用例インデックスが予期しない構造です\n");
#ifdef DEBUG
    if (debug2) {
	printf("num=%ld, cur=%lx, prev=%lx\n", tp->num, val, tp->val);
    }
#endif
    return ERR;
}

/*
 * twclose - 作業ファイルセットをクローズする
 */
int
twclose(tn)
int	tn;
{
    TFTBL	*tp;

    tp = &tftbl[tn];
#ifdef DEBUG
    if (debug2) {
	printf("twclose(%d): n0=%d, n1=%ld, n2=%ld\n",
	    tn, tp->n0, tp->n1, tp->n2);
    }
#endif
    switch (tp->use) {
    case TF_USE0:
	if (tp->n0x > 0) {
	    /*
	     * tt0の中に成句・用例の境界がある
	     * tt0をソートする必要がある
	     */
	    qsort((uchr *)tp->tt0, (size_t)tp->n0, sizeof(long), lcmp);
	}
	break;
    case TF_USE12:
	/*
	 * tf1に成句、tf2に用例がある
	 */
	if (fclose(tp->fp2) == EOF)
	    goto closeerr;
	tp->fp2 = NULL;
	/* fall thru ... */
    case TF_USE01:
	/*
	 * tt0に成句、tf1に用例がある
	 */
	/* fall thru ... */
    case TF_USE1:
	/*
	 * tf1の中に成句と用例のいずれかまたはその両方がある
	 * 値は単調増加している
	 */
	if (fclose(tp->fp1) == EOF)
	    goto closeerr;
	tp->fp1 = NULL;
	break;
    }
    tp->open = FALSE;
    return OK;
closeerr:
    outstr("ERR: ディスクがいっぱいです\n");
    return ERR;
}

/*
 * tropen - 作業ファイルセットを読み込みオープンする
 */
int
tropen(tn)
int	tn;
{
    TFTBL	*tp;

    tp = &tftbl[tn];
    switch (tp->use) {
    case TF_USE0:
	tp->n0x = 0;
	break;
    case TF_USE01:
	tp->n0x = 0;
	goto open1;
    case TF_USE12:
	if ((tp->fp2 = fopen(tp->tf2, "r")) == NULL)
	    goto openerr;
	tp->v2 = getlval(tp->fp2);
	tp->n2x = 0L;
	/* fall thru ... */
    case TF_USE1:
    open1:
	if ((tp->fp1 = fopen(tp->tf1, "r")) == NULL)
	    goto openerr;
	tp->v1 = getlval(tp->fp1);
	tp->n1x = 0L;
	break;
    }
    tp->open = TRUE;
    return OK;
openerr:
    outstr("ERR: 作業用ファイルがオープンできません\n");
    return ERR;
}

/*
 * tread - 作業ファイルセットから読み込む
 */
long
tread(tn)
int	tn;
{
    long	v;
    TFTBL	*tp;

    tp = &tftbl[tn];
    switch (tp->use) {
    case TF_USE0:
	if (tp->n0x >= tp->n0)
	    return EOF;
	v = tp->tt0[tp->n0x++];
	break;
    case TF_USE01:
	if (tp->n0x >= tp->n0) {
	    if (tp->n1x >= tp->n1) {
		return EOF;
	    } else {
		v = tp->v1;
		tp->v1 = getlval(tp->fp1);
		tp->n1x++;
	    }
	} else {
	    if (tp->n1x >= tp->n1) {
		v = tp->tt0[tp->n0x++];
	    } else {
		if (tp->tt0[tp->n0x] < tp->v1) {
		    v = tp->tt0[tp->n0x++];
		} else {
		    v = tp->v1;
		    tp->v1 = getlval(tp->fp1);
		    tp->n1x++;
		}
	    }
	}
	break;
    case TF_USE1:
	if (tp->n1x >= tp->n1)
	    return EOF;
	v = tp->v1;
	tp->v1 = getlval(tp->fp1);
	tp->n1x++;
	break;
    case TF_USE12:
	if (tp->n1x >= tp->n1) {
	    if (tp->n2x >= tp->n2) {
		return EOF;
	    } else {
		v = tp->v2;
		tp->v2 = getlval(tp->fp2);
		tp->n2x++;
	    }
	} else {
	    if (tp->n2x >= tp->n2) {
		v = tp->v1;
		tp->v1 = getlval(tp->fp1);
		tp->n1x++;
	    } else {
		if (tp->v1 < tp->v2) {
		    v = tp->v1;
		    tp->v1 = getlval(tp->fp1);
		    tp->n1x++;
		} else {
		    v = tp->v2;
		    tp->v2 = getlval(tp->fp2);
		    tp->n2x++;
		}
	    }
	}
	break;
    }
    return v;
}

/*
 * trclose - 作業ファイルセットをクローズする
 */
int
trclose(tn)
int	tn;
{
    TFTBL	*tp;

    tp = &tftbl[tn];
    switch (tp->use) {
    case TF_USE0:
	break;
    case TF_USE12:
	fclose(tp->fp2);
	tp->fp2 = NULL;
	/* fall thru ... */
    case TF_USE01:
    case TF_USE1:
	fclose(tp->fp1);
	tp->fp1 = NULL;
	break;
    }
    tp->open = FALSE;
    return OK;
}

/*
 * tisopen - 作業ファイルセットはオープンされているか?
 */
int
tisopen(tn)
int	tn;
{
    return tftbl[tn].open;
}

/*
 * tswap - TFTBLを交換する
 */
void
tswap(tn0, tn1)
int	tn0, tn1;
{
    TFTBL	tftmp;

    tftmp = tftbl[tn0];
    tftbl[tn0] = tftbl[tn1];
    tftbl[tn1] = tftmp;
}

/*
 * tfree - tt0を解放する
 */
void
tfree(tn)
int	tn;
{
    TFTBL	*tp;

    if (tftbl == NULL)
	return;
    tp = &tftbl[tn];
    if (tp->tt0) {
	free((char *)tp->tt0);
	tp->tt0 = NULL;
    }
}

/*
 * lcmp - long値どうしの比較(qsort用)
 */
int
lcmp(p1, p2)
long	*p1, *p2;
{
    if (*p1 < *p2)
	    return -1;
    if (*p1 > *p2)
	    return 1;
    return 0;
}

/*
 * getlval - ファイルから1行の16進文字列を読んでlongとして返す
 */
long
getlval(fp)
FILE	*fp;
{
    uchr	tmp[16];
    long	l;

    if (fgets(tmp, 16, fp) == NULL)
	return (long)EOF;
    sscanf(tmp, "%lx", &l);
    return l;
}

/* -------------------- 検索 -------------------- */

#define	EN_SRCH		0		/* 英単語検索			*/
#define	TR_SRCH		1		/* 訳語検索			*/
#define	ID_SRCH		2		/* 成句・用例検索		*/

#define	X_MATCH		0		/* 完全一致検索			*/
#define	F_MATCH		1		/* 前方一致検索			*/
#define	R_MATCH		2		/* 後方一致検索			*/
#define	W_MATCH		3		/* ワイルドカード検索		*/

#define	EN_F_IDX	"enf.idx"	/* 英単語前方一致インデックス	*/
#define	EN_R_IDX	"enr.idx"	/* 英単語後方一致インデックス	*/
#define	TR_F_IDX	"trf.idx"	/* 訳語前方一致インデックス	*/
#define	TR_R_IDX	"trr.idx"	/* 訳語後方一致インデックス	*/
#define	EN_LIST		"en.dat"	/* 英単語リスト			*/
#define	TR_LIST		"tr.dat"	/* 訳語リスト			*/
#define	ID_IDX		"id.idx"	/* 成句・用例インデックス	*/
#define	ID_LIST		"id.dat"	/* 成句・用例リスト		*/
#define	EN_M_LIST	"miden.txt"	/* 英単語中間一致リスト		*/
#define	TR_M_LIST	"midtr.txt"	/* 訳語中間一致リスト		*/

#define	INODETOP	0x00000100L	/* インデックスの先頭ノード位置	*/
#define	LENTTOP		0x00000100L	/* リストエントリーの先頭位置	*/

/*
 * インデックスエントリー
 */
typedef	struct _ient {
    uchr	lstpos[4];		/* リストファイル中の位置	*/
    uchr	next[4];		/* 下位ノードのノード番号	*/
} IENT;

/*
 * インデックスノード
 */
typedef	struct _inode {
    uchr	pageno[4];		/* 不明				*/
    uchr	last[4];		/* ノード中の最終エントリー番号	*/
    IENT	ent[7];			/* ノード中のエントリー		*/
} INODE;

/*
 * リストエントリー
 */
typedef	struct _lent {
    long	dpos;			/* 本文データ位置		*/
    long	dlen;			/* 本文データサイズ		*/
    uchr	fkey[128];		/* 前方一致キー			*/
    uchr	rkey[128];		/* 後方一致キー			*/
    uchr	kstr[256];		/* 見出し文字列			*/
} LENT;

/*
 * 成句・用例リストエントリー
 */
typedef	struct _ilent {
    unt		entid;			/* 順序番号			*/
    unt		nent;			/* 同一単語個数???		*/
    long	dpos;			/* 本文データ位置		*/
    uchr	idkey[24];		/* 構成単語文字列(最大17文字)	*/
} ILENT;

int	stype = EN_SRCH;		/* 検索種別(英単語/訳語/成句)	*/
int	mtype = X_MATCH;		/* マッチ(完全/前方/後方/中間)	*/
uchr	*idxfile = NULL;		/* 検索用インデックスファイル	*/
uchr	*lstfile = NULL;		/* 検索用リストファイル		*/

INODE	inode;				/* 作業用			*/
LENT	lent;				/* 作業用			*/
ILENT	ilent;				/* 作業用			*/

long	val();
uchr	*fmtkstr();
void	getlistkey();
int	getfirstent();
void	getlistent();
int	index_search();
void	getilistent();
int	idiom_search();
uchr	*mgetent();
int	match();
int	wild_search();
uchr	*getword();
int	search();

/*
 * val - 文字列中の4バイト値をlongに変換
 */
long
val(str)
register uchr	*str;
{
    return ((long)str[3] << 24) +
	   ((long)str[2] << 16) +
	   ((long)str[1] << 8) +
	   (long)str[0];
}

/*
 * fmtkstr - 見出し文字列中の「@」を改行に変換する
 */
uchr *
fmtkstr(kstr)
uchr	*kstr;
{
    int		in;
    uchr	*p, *q;
    static uchr	knew[256];

    in = FALSE;
    p = kstr;
    q = knew;
    while (*p) {
	if (*p == '<') {
	    if (!strncmp(p, "<G99>", 5))
		in = TRUE;
	    else if (!strncmp(p, "</G99>", 6))
		in = FALSE;
	    while (*p != '>')
		*q++ = *p++;
	    *q++ = *p++;
	    continue;
	}
	if (!in && *p == '@') {
	    while (*p == '@')
		p++;
	    *q++ = '\n';
	    continue;
	}
	*q++ = *p++;
    }
    if (q > knew && q[-1] == '\n')
	q--;
    *q = '\0';
    return knew;
}

/*
 * getlistkey - リストファイルから見出し文字列を取り出す
 */
void
getlistkey(lfp, buf, mtype)
BFILE	*lfp;
uchr	*buf;
int	mtype;
{
    int		len;
    uchr	*p;

    bgetc(lfp);
    bgetc(lfp);
    if (mtype == R_MATCH) {
	len = bgetc(lfp);
	while (len-- > 0)
	    bgetc(lfp);
	bgetc(lfp);
	bgetc(lfp);
    }
    len = bgetc(lfp);
    p = buf;
    while (len-- > 0)
	*p++ = bgetc(lfp);
    *p = '\0';
}

/*
 * getfirstent - キーにマッチする最初のエントリーを探す
 */
int
getfirstent(ifp, lfp, word, ip, np)
BFILE	*ifp, *lfp;
uchr	*word;
INODE	*ip;
int	*np;
{
    int		i, n, last, len, cmp;
    long	ipos, lpos, nodeno;
    uchr	key[128];
#ifdef DEBUG
    int		first, level;
#endif

    ipos = INODETOP;
#ifdef DEBUG
    level = 0;
#endif
    /*
     * トップ〜中間段の処理
     */
    for (;;) {
	if (bseek(ifp, ipos) == ERR ||
	    bread(ifp, (uchr *)ip, sizeof(INODE)) < sizeof(INODE)) {
	    outstr("PANIC: インデックスファイルに不整合があります\n");
	    return ERR;
	}
	if (val(ip->ent[0].next) == 0L) {
	    /*
	     * 最終段→別の方法で処理する
	     */
	    break;
	}
#ifdef DEBUG
	if (debug) {
	    printf("<level %d>\n", ++level);
	    printf("pageno=%08lx, last=%08lx\n",
		val(ip->pageno), val(ip->last));
	    for (i = 0; i <= (int)val(ip->last); i++) {
		lpos = val(ip->ent[i].lstpos);
		printf("e[%d].lstpos=%08lx, e[%d].next=%08lx",
		    i, lpos, i, val(ip->ent[i].next));
		if (lpos > 0L) {
		    bseek(lfp, lpos);
		    getlistkey(lfp, key, mtype);
		    printf(", key=[%s]", key);
		}
		printf("\n");
	    }
	}
#endif
	last = (int)val(ip->last);
	for (i = 0; i < last; i++) {
	    lpos = val(ip->ent[i+1].lstpos);
	    if (bseek(lfp, lpos) == ERR) {
		outstr("PANIC: リストファイルに不整合があります\n");
		return ERR;
	    }
	    getlistkey(lfp, key, mtype);
#ifdef DEBUG
	    if (debug)
		printf("i=%d, word=[%s], key=[%s]\n", i, word, key);
#endif
	    if (strcmp(word, key) <= 0)
		break;
	}
	nodeno = val(ip->ent[i].next);
	ipos = (nodeno - 1L) * sizeof(INODE) + INODETOP;
#ifdef DEBUG
	if (debug)
	    printf("nodeno=%08lx, ipos=%08lx\n", nodeno, ipos);
#endif
    }
    /*
     * 最終段の処理
     */
    len = strlen(word);
    last = (int)val(ip->last);
    n = 0;
#ifdef DEBUG
    first = TRUE;
#endif
    for (;;) {
	if (n > last) {
	    if (bread(ifp, (uchr *)ip, sizeof(INODE)) < sizeof(INODE) ||
		val(ip->pageno) == 0L) {
		/*
		 * インデックスの終端に達した
		 */
		return ERR;
	    }
	    last = (int)val(ip->last);
	    n = 0;
	}
#ifdef DEBUG
	if (debug && n == 0) {
	    if (first) {
		printf("<last level>\n");
		first = FALSE;
	    } else {
		printf("<next node>\n");
	    }
	    printf("pageno=%08lx, last=%08lx\n",
		val(ip->pageno), val(ip->last));
	    for (i = 0; i <= (int)val(ip->last); i++) {
		lpos = val(ip->ent[i].lstpos);
		bseek(lfp, lpos);
		getlistkey(lfp, key, mtype);
		printf("e[%d].lstpos=%08lx, key=[%s]\n", i, lpos, key);
	    }
	}
#endif
	lpos = val(ip->ent[n].lstpos);
	if (bseek(lfp, lpos) == ERR) {
	    outstr("PANIC: リストファイルに不整合があります\n");
	    return ERR;
	}
	getlistkey(lfp, key, mtype);
	cmp = strncmp(word, key, len);
	if (cmp < 0)
	    return ERR;
	if (cmp == 0) {
	    if (mtype == X_MATCH && strcmp(word, key) != 0)
		return ERR;
	    *np = n;
	    return OK;
	}
	n++;
    }
    /* NOTREACHED */
}

/*
 * getlistent - リストファイルから見出しデータを取り出す
 */
void
getlistent(lfp, lp)
BFILE	*lfp;
LENT	*lp;
{
    int		i, len;
    uchr	*p;

    bgetc(lfp);
    bgetc(lfp);
    len = bgetc(lfp);
    p = lp->fkey;
    while (len-- > 0)
	*p++ = bgetc(lfp);
    *p = '\0';
    bgetc(lfp);
    bgetc(lfp);
    len = bgetc(lfp);
    p = lp->rkey;
    while (len-- > 0)
	*p++ = bgetc(lfp);
    *p = '\0';
    bgetc(lfp);
    bgetc(lfp);
    lp->dpos = 0L;
    for (i = 0; i < 32; i += 8)
	lp->dpos |= ((long)bgetc(lfp) << i);
    bgetc(lfp);
    bgetc(lfp);
    lp->dlen = 0L;
    for (i = 0; i < 32; i += 8)
	lp->dlen |= ((long)bgetc(lfp) << i);
    for (i = 0; i < 8; i++)
	bgetc(lfp);
    len = bgetc(lfp);
    p = lp->kstr;
    while (len-- > 0)
	*p++ = bgetc(lfp);
    *p = '\0';
}

/*
 * index_search - 完全/前方/後方一致でインデックス検索を行う
 */
int
index_search(dicdir, showall, word)
uchr	*dicdir, *word;
int	showall;
{
    int		n, last, len, cmp, ret;
    long	lpos;
    uchr	*keyp;
    BFILE	*ifp, *lfp, *bfp;
#ifdef DEBUG
    int		i;
    uchr	key[128];
#endif

    ret = OK;
    ifp = lfp = bfp = NULL;
    if ((ifp = dic_open(dicdir, idxfile)) == NULL ||
	(lfp = dic_open(dicdir, lstfile)) == NULL)
	goto err;
    if (getfirstent(ifp, lfp, word, &inode, &n) == ERR)
	goto err;
    keyp = (mtype == R_MATCH)? lent.rkey: lent.fkey;
    len = strlen(word);
    last = (int)val(inode.last);
    for (;;) {
	if (n > last) {
	    if (bread(ifp, (uchr *)&inode, sizeof(INODE)) < sizeof(INODE) ||
		val(inode.pageno) == 0L) {
		/*
		 * インデックスの終端に達した
		 */
		break;
	    }
	    last = (int)val(inode.last);
	    n = 0;
	}
#ifdef DEBUG
	if (debug && n == 0) {
	    printf("<next node>\n");
	    printf("pageno=%08lx, last=%08lx\n",
		val(inode.pageno), val(inode.last));
	    for (i = 0; i <= (int)val(inode.last); i++) {
		lpos = val(inode.ent[i].lstpos);
		bseek(lfp, lpos);
		getlistkey(lfp, key, mtype);
		printf("e[%d].lstpos=%08lx, key=[%s]\n", i, lpos, key);
	    }
	}
#endif
	lpos = val(inode.ent[n].lstpos);
	if (bseek(lfp, lpos) == ERR) {
	    outstr("PANIC: リストファイルに不整合があります\n");
	    goto err;
	}
	getlistent(lfp, &lent);
	cmp = strncmp(word, keyp, len);
	if (cmp < 0)
	    break;
	if (cmp > 0) {
	    n++;
	    continue;
	}
	if (showall || mtype == X_MATCH) {
	    if (mtype == X_MATCH && strcmp(word, keyp) != 0)
		break;
	    if (bfp == NULL) {
		if ((bfp = dic_open(dicdir, MAIN_DAT)) == NULL)
		    goto err;
	    } else {
		outstr("\n");
	    }
#ifdef DEBUG
	    if (debug)
		printf("show_dat: key=[%s], dpos=%lx, dlen=%lx\n",
		    lent.fkey, lent.dpos, lent.dlen);
#endif
	    if (show_data(bfp, lent.dpos, lent.dlen) == ERR)
		goto err;
	} else {
	    outstr(fmtkstr(lent.kstr));
	    if (stype == TR_SRCH) {
		outstr(" [");
		outstr(lent.fkey);
		outstr("]");
	    }
	    outstr("\n");
	}
	n++;
    }
    goto done;
err:
    ret = ERR;
done:
    if (ifp != NULL)
	bclose(ifp);
    if (lfp != NULL)
	bclose(lfp);
    if (bfp != NULL)
	bclose(bfp);
    return ret;
}

/*
 * getilistent - 成句・用例リストファイルから構成単語データを取り出す
 */
void
getilistent(lfp, ip)
BFILE	*lfp;
ILENT	*ip;
{
    int		i, len;
    uchr	*p;

    ip->entid = bgetc(lfp);
    ip->entid += (bgetc(lfp) << 8);
    len = bgetc(lfp);
    p = ip->idkey;
    while (len-- > 0)
	*p++ = bgetc(lfp);
    *p = '\0';
    bgetc(lfp);
    bgetc(lfp);
    ip->dpos = 0L;
    for (i = 0; i < 32; i += 8)
	ip->dpos |= ((long)bgetc(lfp) << i);
    bgetc(lfp);
    bgetc(lfp);
    ip->nent = bgetc(lfp);
    ip->nent += (bgetc(lfp) << 8);
    for (i = 0; i < 4; i++)
	bgetc(lfp);
}

/*
 * idiom_search - 成句・用例検索を行う
 */
int
idiom_search(dicdir, ac, av)
uchr	*dicdir;
int	ac;
uchr	**av;
{
    int		i, n, last, cmp, ret, nomore;
    long	lpos, odpos;
    long	l0, l1, num0, num1, num2, n0, n1;
    uchr	*word;
    BFILE	*ifp, *lfp, *bfp;
#ifdef DEBUG
    uchr	key[128];
#endif

    /*
     * 前準備
     */
    for (i = 0; i < ac; i++) {
	/*
	 * 検索指定文字列が正常かどうか
	 * 先にチェックだけしておく
	 */
	if (getword(tosjis(av[i])) == NULL)
	    return ERR;
    }
    ret = OK;
    ifp = lfp = bfp = NULL;
    if ((ifp = dic_open(dicdir, idxfile)) == NULL ||
	(lfp = dic_open(dicdir, lstfile)) == NULL ||
	(bfp = dic_open(dicdir, MAIN_DAT)) == NULL)
	goto err;
    if (setuptmp() == ERR) {
	outstr("ERR: メモリーが足りません\n");
	goto err;
    }
    /*
     * 最初の単語を検索する
     * 結果は作業セット0に書き出す
     * ただし最後の単語ならそのまま表示して終わり
     */
    word = getword(tosjis(*av));
    if (getfirstent(ifp, lfp, word, &inode, &n) == ERR)
	goto err;
#ifdef DEBUG
    if (debug2)
	printf("search1 start: word=%s\n", word);
#endif
    nomore = (ac == 1);
    num0 = 0L;
    odpos = 0L;
    last = (int)val(inode.last);
    for (;;) {
	if (n > last) {
	    if (bread(ifp, (uchr *)&inode, sizeof(INODE)) < sizeof(INODE) ||
		val(inode.pageno) == 0L)
		break;
	    last = (int)val(inode.last);
	    n = 0;
	}
#ifdef DEBUG
	if (debug && n == 0) {
	    printf("<next node>\n");
	    printf("pageno=%08lx, last=%08lx\n",
		val(inode.pageno), val(inode.last));
	    for (i = 0; i <= (int)val(inode.last); i++) {
		lpos = val(inode.ent[i].lstpos);
		bseek(lfp, lpos);
		getlistkey(lfp, key, X_MATCH);
		printf("e[%d].lstpos=%08lx, key=[%s]\n", i, lpos, key);
	    }
	}
#endif
	lpos = val(inode.ent[n].lstpos);
	if (bseek(lfp, lpos) == ERR) {
	    outstr("PANIC: リストファイルに不整合があります\n");
	    goto err;
	}
	getilistent(lfp, &ilent);
	cmp = strcmp(word, ilent.idkey);
	if (cmp < 0)
	    break;
	if (cmp > 0) {
	    n++;
	    continue;
	}
#ifdef DEBUG
	if (debug)
	    printf("idkey=[%s], entid=%04x, nent=%04x, dpos=%08lx\n",
		ilent.idkey, ilent.entid, ilent.nent, ilent.dpos);
#endif
	if (ilent.dpos == odpos) {
	    n++;
	    continue;
	}
	odpos = ilent.dpos;
	if (nomore) {
	    /*
	     * 検索指定文字列が1つだけだった
	     */
#ifdef DEBUG
	    if (debug2) {
		printf("dpos=%08lx\n", ilent.dpos);
		num0++;
		n++;
		continue;
	    }
#endif
	    if (show_idiom(bfp, ilent.dpos) == ERR)
		goto err;
	    num0++;
	    n++;
	    continue;
	}
	/*
	 * 検索結果を記録する
	 */
	if (!tisopen(0) && twopen(0) == ERR ||
	    twrite(0, ilent.dpos) == ERR)
	    goto err;
	num0++;
	n++;
    }
#ifdef DEBUG
    if (debug2)
	printf("num0=%ld\n", num0);
#endif
    if (tisopen(0) && twclose(0) == ERR)
	goto err;
    if (nomore || num0 == 0L) {
	/*
	 * 最後の単語か、結果が0件だった
	 */
	goto done;
    }
    ac--, av++;
    /*
     * 残りの単語を検索する
     * 結果は作業セット1に書き出す
     * 作業セット0と作業セット1を突き合わせて
     * 両方に存在するものだけを作業セット2に書き出す
     * あるいは最後の単語なら結果を表示する
     */
    while (ac > 0) {
	word = getword(tosjis(*av));
	if (getfirstent(ifp, lfp, word, &inode, &n) == ERR)
	    goto err;
#ifdef DEBUG
	if (debug2)
	    printf("search2 start: word=%s\n", word);
#endif
	nomore = (ac == 1);
	num1 = 0L;
	odpos = 0L;
	last = (int)val(inode.last);
	for (;;) {
	    if (n > last) {
		if (bread(ifp, (uchr *)&inode, sizeof(INODE)) < sizeof(INODE) ||
		    val(inode.pageno) == 0L)
		    break;
		last = (int)val(inode.last);
		n = 0;
	    }
#ifdef DEBUG
	    if (debug && n == 0) {
		printf("<next node>\n");
		printf("pageno=%08lx, last=%08lx\n",
		    val(inode.pageno), val(inode.last));
		for (i = 0; i <= (int)val(inode.last); i++) {
		    lpos = val(inode.ent[i].lstpos);
		    bseek(lfp, lpos);
		    getlistkey(lfp, key, X_MATCH);
		    printf("e[%d].lstpos=%08lx, key=[%s]\n", i, lpos, key);
		}
	    }
#endif
	    lpos = val(inode.ent[n].lstpos);
	    if (bseek(lfp, lpos) == ERR) {
		outstr("PANIC: リストファイルに不整合があります\n");
		goto err;
	    }
	    getilistent(lfp, &ilent);
	    cmp = strcmp(word, ilent.idkey);
	    if (cmp < 0)
		break;
	    if (cmp > 0) {
		n++;
		continue;
	    }
#ifdef DEBUG
	    if (debug)
		printf("idkey=[%s], entid=%04x, nent=%04x, dpos=%08lx\n",
		    ilent.idkey, ilent.entid, ilent.nent, ilent.dpos);
#endif
	    if (ilent.dpos == odpos) {
		n++;
		continue;
	    }
	    odpos = ilent.dpos;
	    /*
	     * 検索結果を記録する
	     */
	    if (!tisopen(1) && twopen(1) == ERR ||
	        twrite(1, ilent.dpos) == ERR)
		goto err;
	    num1++;
	    n++;
	}
#ifdef DEBUG
	if (debug2)
	    printf("num1=%ld\n", num1);
#endif
	if (tisopen(1) && twclose(1) == ERR)
	    goto err;
	if (num1 == 0L) {
	    /*
	     * 結果が0件だった
	     */
	    goto done;
	}
	/*
	 * 作業セット0と作業セット1を突き合わせて
	 * 両方に存在するものだけを作業セット2に書き出す
	 * その結果を新たな作業セット0とする
	 */
	if (tropen(0) == ERR || tropen(1) == ERR)
	    goto err;
#ifdef DEBUG
	if (debug2) {
	    printf("compare start\n");
	}
#endif
	n0 = n1 = num2 = 0L;
	l0 = tread(0);
	l1 = tread(1);
	for (;;) {
	    if (l0 < l1) {
		if (++n0 >= num0)
		    break;
		l0 = tread(0);
		continue;
	    }
	    if (l1 < l0) {
		if (++n1 >= num1)
		    break;
		l1 = tread(1);
		continue;
	    }
	    if (nomore) {
#ifdef DEBUG
		if (debug2) {
		    printf("dpos=%08lx\n", l0);
		    goto next;
		}
#endif
		if (show_idiom(bfp, l0) == ERR)
		    goto err;
		goto next;
	    }
	    /*
	     * 検索結果を記録する
	     */
	    if (!tisopen(2) && twopen(2) == ERR ||
		twrite(2, l0) == ERR)
		goto err;
	next:
	    if (++n0 >= num0)
		break;
	    l0 = tread(0);
	    if (++n1 >= num1)
		break;
	    l1 = tread(1);
	    num2++;
	}
#ifdef DEBUG
	if (debug2)
	    printf("num2=%ld\n", num2);
#endif
	trclose(0);
	trclose(1);
	if (tisopen(2) && twclose(2) == ERR)
	    goto err;
	if (num2 == 0L) {
	    /*
	     * 結果が0件だった
	     */
	    goto done;
	}
	tswap(0, 2);
	ac--, av++;
    }
    goto done;
err:
    ret = ERR;
done:
    tfree(0);
    tfree(1);
    tfree(2);
    if (ifp != NULL)
	bclose(ifp);
    if (lfp != NULL)
	bclose(lfp);
    if (bfp != NULL)
	bclose(bfp);
#ifdef DEBUG
    if (debug2)
	return ret;
#endif
    cleantmp();
    return ret;
}

/*
 * mgetent - 中間一致リストを1行読み込む
 */
uchr *
mgetent(bfp, buf)
BFILE	*bfp;
uchr	*buf;
{
    int		c;
    uchr	*p;

    if ((c = bgetc(bfp)) == EOF)
	return NULL;
    p = buf;
    do {
	*p++ = c;
    } while ((c = bgetc(bfp)) != '\r');
    *p = '\0';
    return buf;
}

/*
 * match - 文字列とパターンの照合を行う
 */
int
match(str, pat)
uchr	*str, *pat;
{
    unt		c;
    uchr	*s, *p;

#ifdef DEBUG
    if (debug2) {
	printf("str=\"%s\", pat=\"%s\"\n", str, pat);
    }
#endif
    if (stype == EN_SRCH) {
	/*
	 * 1バイト文字の照合
	 */
	while (*str) {
	    switch (*pat) {
	    case '?':
		/* 
		 * 任意の文字とマッチする
		 */
		break;
	    case '*':
		/*
		 * strの残りを1文字ずつ減らしながら
		 * patの残りとマッチするかどうか調べる
		 */
		s = str;
		do {
		    if (match(s, pat + 1))
			return TRUE;
		} while (*s++);
		break;
	    case '[':
		for (p = pat + 1; *p != ']'; p++) {
		    if (*str == *p)
			break;
		    if (p[1] == '-') {
			if (*str >= *p && *str <= p[2])
			    break;
			p += 2;
		    }
		}
		if (*p == ']') {
		    /*
		     * マッチしなかった
		     */
		    return FALSE;
		}
		while (*p != ']')
		    p++;
		pat = p;
		break;
	    case '\0':
		/* 
		 * パターンが先に尽きた
		 */
		return FALSE;
	    default:
		if (*str != *pat) {
		    /*
		     * マッチしなかった
		     */
		    return FALSE;
		}
		break;
	    }
	    str++;
	    pat++;
	}
	if (*pat == '\0') {
	    /*
	     * ファイル名とパターンが同時に尽きた
	     */
	    return TRUE;
	}
	return FALSE;
    }
    /*
     * 2バイト文字の照合
     */
    while (*str) {
	switch (*pat) {
	case '?':
	    /* 
	     * 任意の文字とマッチする
	     */
	    str += 2;
	    pat++;
	    break;
	case '*':
	    /*
	     * strの残りを1文字ずつ減らしながら
	     * patの残りとマッチするかどうか調べる
	     */
	    s = str;
	    for (;;) {
		if (match(s, pat + 1))
		    return TRUE;
		if (*s == '\0')
		    break;
		s += 2;
	    }
	    str += 2;
	    pat++;
	    break;
	case '[':
	    p = pat + 1;
	    while (*p != ']') {
		if (*str == *p && str[1] == p[1])
		    break;
		if (p[2] == '-') {
		    c = (*str << 8 | str[1]);
		    if (c >= (p[0] << 8 | p[1]) &&
			c <= (p[3] << 8 | p[4]))
			break;
		    p += 3;
		}
		p += 2;
	    }
	    if (*p == ']') {
		/*
		 * マッチしなかった
		 */
		return FALSE;
	    }
	    while (*p != ']') {
		if (*p == '-') {
		    p++;
		    continue;
		}
		p += 2;
	    }
	    str += 2;
	    pat = p + 1;
	    break;
	case '\0':
	    /* 
	     * パターンが先に尽きた
	     */
	    return FALSE;
	default:
	    if (*str != *pat || str[1] != pat[1]) {
		/*
		 * マッチしなかった
		 */
		return FALSE;
	    }
	    str += 2;
	    pat += 2;
	    break;
	}
    }
    if (*pat == '\0') {
	/*
	 * ファイル名とパターンが同時に尽きた
	 */
	return TRUE;
    }
    return FALSE;
}

/*
 * wild_search - ワイルドカード検索を行う
 */
int
wild_search(dicdir, showall, pat)
uchr	*dicdir, *pat;
int	showall;
{
    int		ret, first;
    long	dpos, dlen, odpos;
    uchr	*p;
    BFILE	*lfp, *bfp;
    uchr	buf[256];

    ret = OK;
    lfp = bfp = NULL;
    if ((lfp = dic_open(dicdir, lstfile)) == NULL ||
	(bfp = dic_open(dicdir, MAIN_DAT)) == NULL)
	goto err;
    bseek(lfp, 0L);
    first = TRUE;
    odpos = 0L;
    while (mgetent(lfp, buf) != NULL) {
	p = buf;
	while (*p && *p != '0')
	    p++;
	*p = '\0';
	if (!match(buf, pat))
	    continue;
	sscanf(p+1, "%7lx%lx", &dpos, &dlen);
#ifdef DEBUG
	if (debug)
	    printf("show_dat: key=[%s], dpos=%lx, dlen=%lx\n",
		buf, dpos, dlen);
#endif
	if (showall) {
	    if (first)
		first = FALSE;
	    else
		outstr("\n");
	    if (dpos == odpos)
		continue;
	    if (show_data(bfp, dpos, dlen) == ERR)
		goto err;
	} else {
	    if (show_header(bfp, dpos) == ERR)
		goto err;
	    if (stype == EN_SRCH)
		lower(buf);
	    outstr(" [");
	    outstr(buf);
	    outstr("]\n");
	}
    }
    goto done;
err:
    ret = ERR;
done:
    if (lfp != NULL)
	bclose(lfp);
    if (bfp != NULL)
	bclose(bfp);
    return ret;
}

/*
 * getword - 検索指定文字列を受け取って検索単語その他の設定を行う
 */
uchr *
getword(str)
uchr	*str;
{
    int		n, len, err;
    int		eng, knj, any, meta, bra;
    uchr	*s, *p, *q, ch;
    static uchr	buf[128];

    len = strlen(str);
    eng = knj = any = meta = 0;
    err = 0;
    bra = FALSE;
    s = str;
    while (*s) {
	if (strchr("*?[]-", *s)) {
	    if (*s == '*' && (bra || s[1] == '*') ||
		*s == '?' && bra ||
		*s == '[' && (bra || s[1] == '-' || s[1] == ']') ||
		*s == '-' && (!bra || s[1] == ']') ||
		*s == ']' && !bra) {
		err++;
		break;
	    }
	    if (*s == '[')
		bra = TRUE;
	    else if (*s == ']')
		bra = FALSE;
	    else if (*s == '?')
		any++;
	    s++;
	    meta++;
	    continue;
	}
	if (SJIS1(*s)) {
	    s += 2;
	    knj++;
	    continue;
	}
	s++;
	eng++;
    }
    if (err || bra) {
	outstr("ERR: ワイルドカード検索指定に誤りがあります\n");
	return NULL;
    }
    if (knj > 0 && eng > 0) {
	outstr("ERR: 検索単語に英語と日本語が混在しています\n");
	return NULL;
    }
    if (knj == 0 && eng == 0 && any == 0) {
	outstr("ERR: 検索単語が空です\n");
	return NULL;
    }
    if (stype == ID_SRCH) {
	if (knj) {
	    outstr("ERR: 成句・用例検索で日本語が指定されています\n");
	    return NULL;
	}
	if (mtype != X_MATCH) {
	    outstr("ERR: 成句・用例検索で不完全一致検索が指定されています\n");
	    return NULL;
	}
    }
    if (meta == 0)
	mtype = X_MATCH;
    else if (meta == 1 && str[len-1] == '*')
	mtype = F_MATCH;
    else if (meta == 1 && *str == '*')
	mtype = R_MATCH;
    else
	mtype = W_MATCH;
    if (knj)
	stype = TR_SRCH;
    s = str;
    p = buf;
    if (mtype == F_MATCH || mtype == R_MATCH) {
	len--;
	if (mtype == R_MATCH)
	    s++;
    }
    n = len;
    while (n > 0) {
	if (SJIS1(*s)) {
	    *p++ = *s++;
	    *p++ = *s++;
	    n -= 2;
	    continue;
	}
	if (islower(*s)) {
	    *p++ = toupper(*s);
	    s++;
	} else {
	    *p++ = *s++;
	}
	n--;
    }
    *p = '\0';
    if (mtype == R_MATCH) {
	/*
	 * 検索文字列を逆順にする
	 */
	p = buf;
	while (*p) {
	    if (SJIS1(*p)) {
		ch = *p;
		*p = p[1];
		p[1] = ch;
		p++;
	    }
	    p++;
	}
	p = buf;
	q = buf + len - 1;
	while (p < q) {
	    ch = *p;
	    *p++ = *q;
	    *q-- = ch;
	}
    }
    return buf;
}

/*
 * search - 検索のメインルーチン
 */
int
search(dicdir, showall, ac, av)
uchr	*dicdir;
int	showall;
int	ac;
uchr	**av;
{
    int		ret;
    uchr	*word;

    if ((word = getword(tosjis(*av))) == NULL)
	return ERR;
    switch (stype) {
    case EN_SRCH:
	switch (mtype) {
	case X_MATCH:
	case F_MATCH:
	    idxfile = EN_F_IDX;
	    lstfile = EN_LIST;
	    ret = index_search(dicdir, showall, word);
	    break;
	case R_MATCH:
	    idxfile = EN_R_IDX;
	    lstfile = EN_LIST;
	    ret = index_search(dicdir, showall, word);
	    break;
	case W_MATCH:
	    lstfile = EN_M_LIST;
	    ret = wild_search(dicdir, showall, word);
	    break;
	}
	break;
    case TR_SRCH:
	switch (mtype) {
	case X_MATCH:
	case F_MATCH:
	    idxfile = TR_F_IDX;
	    lstfile = TR_LIST;
	    ret = index_search(dicdir, showall, word);
	    break;
	case R_MATCH:
	    idxfile = TR_R_IDX;
	    lstfile = TR_LIST;
	    ret = index_search(dicdir, showall, word);
	    break;
	case W_MATCH:
	    lstfile = TR_M_LIST;
	    ret = wild_search(dicdir, showall, word);
	    break;
	}
	break;
    case ID_SRCH:
	idxfile = ID_IDX;
	lstfile = ID_LIST;
	ret = idiom_search(dicdir, ac, av);
	break;
    }
    return ret;
}

/* -------------------- メイン -------------------- */

#define	CSRD_ENV	"CSRD"		/* オプションを設定する環境変数	*/

#define	MINWIDTH	20		/* 出力行幅の下限		*/

int	showall = FALSE;		/* 不完全一致でも項目表示するか?*/
int	showver = FALSE;		/* 辞書データバージョン表示	*/
uchr	*dicdir = NULL;			/* 辞書ディレクトリー		*/
uchr	*fmtfile = NULL;		/* 表示書式ファイル		*/
uchr	*gaifile = NULL;		/* 外字代替表記ファイル		*/
uchr	*supstr = NULL;			/* 表示抑制項目			*/

void	usage();
int	parse_opt();
#if defined(UNIX) && defined(RC_PATH)
int	init_rcfile();
#endif
int	init_env();
void	onintr();
int	main();

/*
 * usage - 使用法を表示して終了する
 */
void
usage()
{
    outstr("小学館ランダムハウス英語辞典検索ユーティリティー Ver.");
    outstr(version);
    outstr(" (");
    outstr(date);
    outstr(")\n    Written by Junn Ohta (ohta@src.ricoh.co.jp),");
    outstr(" Public Domain.\n\n使用法: ");
    outstr(progname);
    outstr(" [-d\\<辞書DIR\\>] [-f\\<書式ファイル\\>] [-g\\<外字ファイル\\>]\n");
    outstr("             [-w\\<桁数\\>] [-s\\<非表示項目\\>] [-b[x]] [-a] [-v]");
#ifdef UNIX
    outstr("\n             [-c\\<コード\\>]");
#endif
    outstr(" \\<検索指定\\>\n\n");
    outstr("オプション: (環境変数");
    outstr(CSRD_ENV);
    outstr("で指定することもできます)\n");
    outstr("    -d: 辞書ディレクトリー\n");
    outstr("    -f: 表示書式ファイル\n");
    outstr("    -g: 外字代替表記ファイル\n");
    outstr("    -w: 出力行幅 (-w0:改行なし(標準))\n");
    outstr("    -s: 特定項目を非表示にする\n");
    outstr("        (-st:意味, -se:用例, -si:成句, -so:類語, -sa:すべて)\n");
    outstr("    -b: 発音記号列を点訳する (-bx:[]を取り除く)\n");
    outstr("    -a: 不完全一致でも項目内容を表示する\n");
    outstr("    -v: 辞書データのバージョンを表示する\n");
#ifdef UNIX
    outstr("    -c: 日本語コード (-cj:JIS, -ce:EUC(標準), -cs:シフトJIS)\n");
#endif
    outstr("\n検索指定: (成句・用例検索以外では訳語からも検索できます)\n");
    outstr("    完全一致検索: \\<文字列\\>     ワイルドカード検索: *?[]-を含む文字列\n");
    outstr("    前方一致検索: \\<文字列\\>*    成句・用例検索: -i \\<文字列\\> ...\n");
    outstr("    後方一致検索: *\\<文字列\\>\n");
    exit(1);
}

/*
 * parse_opt - 環境変数またはコマンド行引数で指定されたオプションを
 *	       解析して必要な設定を行う
 */
int
parse_opt(acp, avp)
int	*acp;
uchr	***avp;
{
    int		n, ac;
    uchr	*s, **av;

    ac = *acp;
    av = *avp;
    while (ac > 0 && **av == '-') {
	s = *av + 1;
	switch (*s++) {
	case 'D':
	case 'd':
	    if (!*s) {
		ac--, av++;
		if (ac == 0 || **av == '-')
		    return ERR;
		s = *av;
	    }
	    dicdir = strpool(s);
	    bsltosl(dicdir);
	    n = strlen(dicdir);
	    if (dicdir[n-1] == '/')
		dicdir[n-1] = '\0';
	    break;
	case 'F':
	case 'f':
	    if (!*s) {
		ac--, av++;
		if (ac == 0 || **av == '-')
		    return ERR;
		s = *av;
	    }
	    fmtfile = strpool(s);
	    bsltosl(fmtfile);
	    break;
	case 'G':
	case 'g':
	    if (!*s) {
		ac--, av++;
		if (ac == 0 || **av == '-')
		    return ERR;
		s = *av;
	    }
	    gaifile = strpool(s);
	    bsltosl(gaifile);
	    break;
	case 'A':
	case 'a':
	    showall = TRUE;
	    break;
	case 'W':
	case 'w':
	    if (!*s) {
		ac--, av++;
		if (ac == 0 || **av == '-')
		    return ERR;
		s = *av;
	    }
	    if (!isdigit(*s))
		return ERR;
	    width = atoi(s);
	    if (width != 0 && width < MINWIDTH)
		width = MINWIDTH;
	    break;
	case 'B':
	case 'b':
	    do_braille = TRUE;
	    if (*s == 'x' || *s == 'X')
		nobracket = TRUE;
	    else
		nobracket = FALSE;
	    break;
#ifdef UNIX
	case 'C':
	case 'c':
	    if (!*s) {
		ac--, av++;
		if (ac == 0 || **av == '-')
		    return ERR;
		s = *av;
	    }
	    switch (*s) {
	    case 'J':
	    case 'j':
		kcode = KC_JIS;
		if (s[1] && s[2]) {
		    jiskanji = s[1];
		    jisalpha = s[2];
		}
		break;
	    case 'E':
	    case 'e':
		kcode = KC_EUC;
		break;
	    case 'S':
	    case 's':
		kcode = KC_SJIS;
		break;
	    default:
		return ERR;
	    }
	    break;
#endif
	case 'S':
	case 's':
	    if (!*s) {
		ac--, av++;
		if (ac == 0 || **av == '-')
		    return ERR;
		s = *av;
	    }
	    supstr = strpool(s);
	    break;
	case 'I':
	case 'i':
	    stype = ID_SRCH;
	    break;
	case 'V':
	case 'v':
	    showver = TRUE;
	    break;
	case 'R':
	case 'r':
	    rawmode = TRUE;
	    break;
#ifdef DEBUG
	case 'Z':
	case 'z':
	    if (*s == 'z' || *s == 'Z')
		debug2 = TRUE;
	    else
		debug = TRUE;
	    break;
#endif
	default:
	    return ERR;
	}
	ac--, av++;
    }
    *acp = ac;
    *avp = av;
    return OK;
}

#if defined(UNIX) && defined(RC_PATH)
/*
 * init_rcfile - ファイルで指定されたオプションを解釈する
 *		 Contributed by 野首 貴嗣 (Nokubi Takatsugu)
 */
int
init_rcfile()
{
    int		ec;
    uchr	*env, *s, **ev, **pp;
    FILE	*fp;
    struct stat	st;

    if (stat(RC_PATH, &st) < 0)
	return OK;
    if ((fp = fopen(RC_PATH, "r")) == NULL)
	return ERR;
    env = (uchr *)malloc(st.st_size + 1);
    if (env == NULL)
	return ERR;
    if (fread(env, st.st_size, 1, fp) < 1)
	return ERR;
    *(env + st.st_size - 1) = '\0';
    fclose(fp);
    ec = 1;
    for (s = env; *s; s++) {
	if (*s == ' ' || *s == '\t')
	    ec++;
    }
    ev = (uchr **)malloc(sizeof(uchr *) * (ec + 1));
    pp = ev;
    while ((s = strtok(env, " \t")) != NULL) {
	*pp++ = s;
	env = NULL;
    }
    *pp = NULL;
    pp = ev;
    if (parse_opt(&ec, &ev) != OK) {
	outstr("ERR: ファイル ");
	outstr(RC_PATH);
	outstr(" に誤りがあります\n");
	return ERR;
    }
    free((char *)pp);
    free((char *)env);
    return OK;
}
#endif

/*
 * init_env - 環境変数CSRDで指定されたオプションを解釈する
 */
int
init_env()
{
    int		ec;
    uchr	*env, *s, **ev, **pp;

    if ((env = getenv(CSRD_ENV)) == NULL)
	return OK;
    ec = 1;
    for (s = env; *s; s++) {
	if (*s == ' ' || *s == '\t')
	    ec++;
    }
    ev = (uchr **)malloc(sizeof(uchr *) * (ec + 1));
    pp = ev;
    while ((s = strtok(env, " \t")) != NULL) {
	*pp++ = s;
	env = NULL;
    }
    *pp = NULL;
    pp = ev;
    if (parse_opt(&ec, &ev) != OK) {
	outstr("ERR: 環境変数 ");
	outstr(CSRD_ENV);
	outstr(" に誤りがあります\n");
	return ERR;
    }
    free((char *)pp);
    return OK;
}

/*
 * onintr - 割り込み時の終了処理
 */
void
onintr()
{
    outchar(0);	/* JIS漢字モードならASCIIに戻す(UNIX) */
    cleantmp();	/* 成句・用例検索用の一時ファイルを削除する */
    exit(0);
}

/*
 * main - メインルーチン
 */
int
main(ac, av)
int	ac;
uchr	**av;
{
    signal(SIGINT, onintr);
#if defined(UNIX) && defined(RC_PATH)
    if (init_rcfile() != OK)
	exit(1);
#endif
    if (init_env() != OK)
	exit(1);
    ac--, av++;
    if (parse_opt(&ac, &av) != OK)
	usage();
    if (!showver && (ac == 0 || stype != ID_SRCH && ac > 1))
	usage();
    if (dicdir == NULL) {
	outstr("ERR: 辞書ディレクトリーが設定されていません\n");
	exit(1);
    }
    if (showver) {
	show_version(dicdir);
	exit(0);
    }
    if (init_alt() == ERR)
	exit(1);
    if (gaifile != NULL) {
	if (load_gaiji(gaifile) == ERR)
	    exit(1);
    }
    if (fmtfile != NULL) {
	if (load_format(fmtfile) == ERR)
	    exit(1);
    }
    if (suppress(supstr) == ERR) {
	outstr("ERR: 表示抑制指定(-sオプション)に誤りがあります\n");
	exit(1);
    }
    if (search(dicdir, showall, ac, av) == ERR)
	exit(1);
    exit(0);
}


syntax highlighted by Code2HTML, v. 0.9.1