/*
 *	dict.c
 *
 *	dictionary accessing module
 */

#include <fcntl.h>
#include "fd.h"
#include "termio.h"
#include "func.h"
#include "roman.h"
#include "hinsi.h"

#ifndef	_NOIME

#define	DICTTBL		"fd-dict.tbl"
#define	MAXHINSI	16
#define	FREQMAGIC	0x4446
#define	FREQVERSION	0x0100
#define	USERFREQBIAS	16
#define	freqbias(n)	(((long)(n) + USERFREQBIAS) * USERFREQBIAS)
#define	getword(s, n)	(((u_short)((s)[(n) + 1]) << 8) | (s)[n])
#define	getdword(s, n)	(((u_long)getword(s, (n) + 2) << 16) | getword(s, n))
#define	skread(f,o,s,n)	(Xlseek(f, o, L_SET) >= (off_t)0 \
			&& sureread(f, s, n) == n)

#ifndef	O_BINARY
#define	O_BINARY	0
#endif
#ifndef	L_SET
# ifdef	SEEK_SET
# define	L_SET	SEEK_SET
# else
# define	L_SET	0
# endif
#endif	/* !L_SET */
#ifndef	L_INCR
# ifdef	SEEK_CUR
# define	L_INCR	SEEK_CUR
# else
# define	L_INCR	1
# endif
#endif	/* !L_INCR */

typedef struct _kanjitable {
	u_short *kbuf;
	u_char klen;
	u_char len;
	u_char match;
	u_char kmatch;
	u_short freq;
	u_short hinsi[MAXHINSI];
	long ofs;
} kanjitable;

static int NEAR fgetbyte __P_((u_char *, int));
static int NEAR fgetword __P_((u_short *, int));
static int NEAR fgetdword __P_((long *, int));
static int NEAR fgetstring __P_((kanjitable *, int));
static off_t NEAR fgetoffset __P_((long, int));
static int NEAR fgetjisbuf __P_((kanjitable *, long, int));
static int NEAR fgethinsi __P_((u_short [], int));
static int NEAR fgetfreqbuf __P_((kanjitable *, long, int));
static int NEAR _fchkhinsi __P_((int, u_short [], int));
static int NEAR fchkhinsi __P_((u_short [], u_short [],
		u_short [], u_short [], int));
static int NEAR opendicttbl __P_((char *));
static u_char *NEAR newhinsitbl __P_((ALLOC_T));
static VOID NEAR readdicttable __P_((int));
static int NEAR fputbyte __P_((int, int));
static int NEAR fputword __P_((u_int, int));
static int NEAR fputdword __P_((long, int));
static int NEAR fputstring __P_((kanjitable *, int));
static lockbuf_t *NEAR openfreqtbl __P_((char *, int));
static int NEAR getfreq __P_((kanjitable *, kanjitable *));
static int NEAR copyuserfreq __P_((kanjitable *, kanjitable *, int, int));
static int cmpdict __P_((CONST VOID_P, CONST VOID_P));
static int cmpfreq __P_((CONST VOID_P, CONST VOID_P));
static long NEAR addkanji __P_((long, kanjitable **, kanjitable *));
static VOID freekanji __P_((kanjitable *));
static long NEAR addkanjilist __P_((long, kanjitable **,
		long, kanjitable *, kanjitable *, int));
static off_t NEAR nextofs __P_((off_t, int));
static long NEAR _searchdict __P_((long, kanjitable **, kanjitable *, int));
static long NEAR uniqkanji __P_((long, kanjitable *));

char *dicttblpath = NULL;
int imebuffer = 0;

static kanjitable *kanjilist = NULL;
static u_char *hinsiindexbuf = NULL;
static u_char *hinsitblbuf = NULL;
static long dicttblent = 0L;
static long freqtblent = 0L;
static int hinsitblent = 0;
static off_t hinsitblofs = (off_t)0;


static int NEAR fgetbyte(cp, fd)
u_char *cp;
int fd;
{
	if (sureread(fd, cp, 1) != 1) return(-1);

	return(0);
}

static int NEAR fgetword(wp, fd)
u_short *wp;
int fd;
{
	u_char buf[2];

	if (sureread(fd, buf, 2) != 2) return(-1);
	*wp = getword(buf, 0);

	return(0);
}

static int NEAR fgetdword(lp, fd)
long *lp;
int fd;
{
	u_char buf[4];

	if (sureread(fd, buf, 4) != 4) return(-1);
	*lp = getdword(buf, 0);

	return(0);
}

static int NEAR fgetstring(kp, fd)
kanjitable *kp;
int fd;
{
	u_short *kbuf;
	u_char c;
	int i;

	kp -> kbuf = NULL;
	if (fgetbyte(&c, fd) < 0) return(-1);
	kp -> klen = c;
	kbuf = (u_short *)malloc2((kp -> klen + 1) * sizeof(u_short));
	for (i = 0; i < kp -> klen; i++) if (fgetword(&(kbuf[i]), fd) < 0) {
		free(kbuf);
		return(-1);
	}
	kbuf[i] = (short)0;
	kp -> kbuf = kbuf;

	return(0);
}

static off_t NEAR fgetoffset(n, fd)
long n;
int fd;
{
	u_char buf[4];
	off_t ofs;

	ofs = (off_t)n * 4 + 4;
	if (!skread(fd, ofs, buf, 4)) return((off_t)-1);
	ofs = (off_t)(dicttblent + 1) * 4 + 4 + getdword(buf, 0);

	return(ofs);
}

static int NEAR fgetjisbuf(kp, n, fd)
kanjitable *kp;
long n;
int fd;
{
	off_t ofs;

	if ((ofs = fgetoffset(n, fd)) < (off_t)0) return(-1);
	if (Xlseek(fd, ofs, L_SET) < (off_t)0) return(-1);

	return(fgetstring(kp, fd));
}

static int NEAR fgethinsi(hinsi, fd)
u_short hinsi[MAXHINSI];
int fd;
{
	u_char c;
	int i, n;

	if (hinsitblent <= 0) n = 0;
	else {
		if (fgetbyte(&c, fd) < 0) return(-1);
		if ((n = c) > MAXHINSI) n = MAXHINSI;
		for (i = 0; i < n; i++)
			if (fgetword(&(hinsi[i]), fd) < 0) return(-1);
	}
	if (n < MAXHINSI) hinsi[n] = MAXUTYPE(u_short);

	return(n);
}

static int NEAR fgetfreqbuf(kp, n, fd)
kanjitable *kp;
long n;
int fd;
{
	u_char buf[4];
	off_t ofs;

	ofs = (off_t)n * 4 + 4 + 4;
	if (!skread(fd, ofs, buf, 4)) return(-1);
	ofs = (off_t)(freqtblent + 1) * 4 + 4 + 4 + getdword(buf, 0);
	if (Xlseek(fd, ofs, L_SET) < (off_t)0) return(-1);

	return(fgetstring(kp, fd));
}

static int NEAR _fchkhinsi(id, hinsi, fd)
int id;
u_short hinsi[MAXHINSI];
int fd;
{
	u_char *cp, *hbuf, buf[2];
	off_t ofs;
	u_short w;
	int i, j, len;

	if (hinsitblent <= 0) return(0);
	if (id >= hinsitblent) return(MAXUTYPE(u_short));

	ofs = (off_t)id * 2;
	if (hinsiindexbuf) ofs = getword(hinsiindexbuf, ofs);
	else {
		ofs += hinsitblofs + 2;
		if (!skread(fd, ofs, buf, 2)) return(-1);
		ofs = getword(buf, 0);
	}

	if (hinsitblbuf) {
		cp = &(hinsitblbuf[ofs]);
		len = getword(cp, 0);
		cp += 2;
		hbuf = NULL;
	}
	else {
		ofs += hinsitblofs + (off_t)(hinsitblent + 1) * 2 + 2;
		if (!skread(fd, ofs, buf, 2)) return(-1);
		if ((len = getword(buf, 0)) <= 0) return(MAXUTYPE(u_short));
		cp = hbuf = (u_char *)malloc2(len * 2);
		if (sureread(fd, hbuf, len * 2) != len * 2) {
			free(hbuf);
			return(-1);
		}
	}

	for (i = 0; i < len; i++, cp += 2) {
		w = getword(cp, 0);
		for (j = 0; j < MAXHINSI; j++) {
			if (hinsi[j] == MAXUTYPE(u_short)) break;
			if (hinsi[j] == w) {
				if (hbuf) free(hbuf);
				return(j);
			}
		}
	}
	if (hbuf) free(hbuf);

	return(MAXUTYPE(u_short));
}

static int NEAR fchkhinsi(fdest, fsrc, bdest, bsrc, fd)
u_short fdest[MAXHINSI], fsrc[MAXHINSI], bdest[MAXHINSI], bsrc[MAXHINSI];
int fd;
{
	u_short fhit[MAXHINSI], bhit[MAXHINSI];
	int i, j, n;

	if (hinsitblent <= 0 || fsrc[0] == MAXUTYPE(u_short)) {
		if (fdest) memcpy((char *)fdest, (char *)fsrc, sizeof(fhit));
		if (bdest) memcpy((char *)bdest, (char *)bsrc, sizeof(bhit));
		return(0);
	}

	for (i = 0; i < MAXHINSI; i++) fhit[i] = bhit[i] = MAXUTYPE(u_short);
	for (i = j = 0; i < MAXHINSI; i++) {
		if (bsrc[i] == MAXUTYPE(u_short)) break;
		n = _fchkhinsi(bsrc[i], fsrc, fd);
		if (n < 0) return(-1);
		if (n < MAXUTYPE(u_short)) {
			fhit[n] = fsrc[n];
			bhit[i] = bsrc[i];
			j++;
		}
	}
	if (!j) return(-1);

	if (fdest) {
		for (i = j = 0; i < MAXHINSI; i++)
			if (fhit[i] < MAXUTYPE(u_short)) fdest[j++] = fhit[i];
		if (j < MAXHINSI) fdest[j] = MAXUTYPE(u_short);
	}
	if (bdest) {
		for (i = j = 0; i < MAXHINSI; i++)
			if (bhit[i] < MAXUTYPE(u_short)) bdest[j++] = bhit[i];
		if (j < MAXHINSI) bdest[j] = MAXUTYPE(u_short);
	}

	return(0);
}

static int NEAR opendicttbl(file)
char *file;
{
	static int fd = -2;
	u_char buf[2];
	char path[MAXPATHLEN];

	if (!file) {
		if (fd >= 0) Xclose(fd);
		fd = -2;
		return(0);
	}

	if (fd >= -1) return(fd);

	if (!dicttblpath || !*dicttblpath) strcpy(path, file);
	else strcatdelim2(path, dicttblpath, file);

	if ((fd = Xopen(path, O_BINARY | O_RDONLY, 0666)) < 0) fd = -1;
	else if (!dicttblent && fgetdword(&dicttblent, fd) < 0) {
		Xclose(fd);
		fd = -1;
	}
	else if (!hinsitblent) {
		if ((hinsitblofs = fgetoffset(dicttblent, fd)) < (off_t)0
		|| !skread(fd, hinsitblofs, buf, 2))
			hinsitblent = -1;
		else hinsitblent = getword(buf, 0);
	}

	return(fd);
}

static u_char *NEAR newhinsitbl(size)
ALLOC_T size;
{
	u_char *tbl;

	if ((tbl = (u_char *)malloc(size))) return(tbl);
	if (imebuffer) imebuffer = 0;

	return(NULL);
}

static VOID NEAR readdicttable(fd)
int fd;
{
	u_char *tbl, buf[2];
	ALLOC_T size;
	off_t ofs;

	if (hinsitblent <= 0) return;

	if (!hinsiindexbuf) {
		size = (ALLOC_T)hinsitblent * 2;
		if (!(tbl = newhinsitbl(size))) return;
		if (!skread(fd, hinsitblofs + 2, tbl, size)) {
			free(tbl);
			return;
		}
		hinsiindexbuf = tbl;
	}

	if (!imebuffer) return;

	if (!hinsitblbuf) {
		ofs = (off_t)hinsitblent * 2 + 2;
		if (!skread(fd, hinsitblofs + ofs, buf, 2)) return;
		size = getword(buf, 0);
		if (!(tbl = newhinsitbl(size))) return;
		if (sureread(fd, tbl, size) != size) {
			free(tbl);
			return;
		}
		hinsitblbuf = tbl;
	}
}

VOID discarddicttable(VOID_A)
{
	if (hinsiindexbuf) {
		free(hinsiindexbuf);
		hinsiindexbuf = NULL;
	}
	if (hinsitblbuf) {
		free(hinsitblbuf);
		hinsitblbuf = NULL;
	}
}

static int NEAR fputbyte(c, fd)
int c, fd;
{
	u_char uc;

	uc = c;
	if (surewrite(fd, &uc, sizeof(uc)) < 0) return(-1);

	return(0);
}

static int NEAR fputword(w, fd)
u_int w;
int fd;
{
	u_char buf[2];

	buf[0] = (w & 0xff);
	buf[1] = ((w >> 8) & 0xff);
	if (surewrite(fd, buf, sizeof(buf)) < 0) return(-1);

	return(0);
}

static int NEAR fputdword(dw, fd)
long dw;
int fd;
{
	u_char buf[4];

	buf[0] = (dw & 0xff);
	buf[1] = ((dw >> 8) & 0xff);
	buf[2] = ((dw >> 16) & 0xff);
	buf[3] = ((dw >> 24) & 0xff);
	if (surewrite(fd, buf, sizeof(buf)) < 0) return(-1);

	return(0);
}

static int NEAR fputstring(kp, fd)
kanjitable *kp;
int fd;
{
	int i;

	if (fputbyte(kp -> klen, fd) < 0) return(-1);
	for (i = 0; i < kp -> klen; i++)
		if (fputword(kp -> kbuf[i], fd) < 0) return(-1);

	return(0);
}

static lockbuf_t *NEAR openfreqtbl(file, flags)
char *file;
int flags;
{
	static lockbuf_t *lck = NULL;
	u_short w;

	if (!file) {
		lockclose(lck);
		lck = NULL;
		return(NULL);
	}

	if (lck) return(lck);
	file = evalpath(strdup2(file), 0);

	freqtblent = 0L;
	lck = lockopen(file, flags, 0666);
	if (!lck || lck -> fd < 0) /*EMPTY*/;
	else if (fgetword(&w, lck -> fd) < 0 || w != FREQMAGIC
	|| fgetword(&w, lck -> fd) < 0 || w != FREQVERSION
	|| fgetdword(&freqtblent, lck -> fd) < 0) {
		lockclose(lck);
		lck = NULL;
	}

	if (!lck) {
		lck = (lockbuf_t *)malloc2(sizeof(lockbuf_t));
		lck -> fd = -1;
		lck -> fp = NULL;
		lck -> name = NULL;
		lck -> flags = LCK_INVALID;
	}

	return(lck);
}

static int NEAR getfreq(kp1, kp2)
kanjitable *kp1, *kp2;
{
	kanjitable tmp;
	lockbuf_t *lck;
	long ofs, min, max;
	int n;

	kp1 -> ofs = -1L;
	lck = openfreqtbl(FREQFILE, O_BINARY | O_RDONLY);
	if (lck -> fd < 0) return(0);

	min = -1L;
	max = freqtblent;
	for (;;) {
		ofs = (min + max) / 2;
		if (ofs <= min) {
			kp1 -> ofs = min + 1;
			return(0);
		}
		else if (ofs >= max) {
			kp1 -> ofs = max;
			return(0);
		}
		else if (fgetfreqbuf(&tmp, ofs, lck -> fd) < 0) return(-1);

		n = cmpdict(kp1, &tmp);
		free(tmp.kbuf);
		if (n > 0) min = ofs;
		else if (n < 0) max = ofs;
		else if (fgetstring(&tmp, lck -> fd) < 0) return(-1);
		else {
			n = cmpdict(kp2, &tmp);
			free(tmp.kbuf);
			if (n > 0) min = ofs;
			else if (n < 0) max = ofs;
			else break;
		}
	}

	kp1 -> ofs = ofs;
	if (fgetword(&(kp1 -> freq), lck -> fd) < 0) return(-1);

	return(1);
}

static int NEAR copyuserfreq(kp1, kp2, fdin, fdout)
kanjitable *kp1, *kp2;
int fdin, fdout;
{
	kanjitable tmp;
	ALLOC_T size;
	long ofs, index, ent;
	int n, skip;

	size = 1 + (kp1 -> klen * 2) + 1 + (kp2 -> klen * 2) + 2;
	skip = 0;
	if (fdin < 0) {
		ent = kp1 -> ofs = 0L;
		kp1 -> freq = (u_short)1;
	}
	else {
		ent = freqtblent;
		n = getfreq(kp1, kp2);
		if (n < 0) return(-1);
		else if (!n) kp1 -> freq = (u_short)1;
		else {
			skip++;
			if (kp1 -> freq < MAXUTYPE(u_short)) kp1 -> freq++;
		}
		if (Xlseek(fdin, (off_t)1 * 4 + 4 + 4, L_SET) < (off_t)0)
			return(-1);
	}

	if (fputword(FREQMAGIC, fdout) < 0) return(-1);
	if (fputword(FREQVERSION, fdout) < 0) return(-1);
	if (fputdword(ent + 1 - skip, fdout) < 0) return(-1);
	index = 0L;
	if (fputdword(index, fdout) < 0) return(-1);
	for (ofs = 1; ofs <= kp1 -> ofs; ofs++) {
		if (fgetdword(&index, fdin) < 0) return(-1);
		if (fputdword(index, fdout) < 0) return(-1);
	}
	if (fputdword(index + size, fdout) < 0) return(-1);
	if (skip) {
		if (fgetdword(&index, fdin) < 0) return(-1);
		size = (ALLOC_T)0;
		ofs++;
	}
	for (; ofs <= ent; ofs++) {
		if (fgetdword(&index, fdin) < 0) return(-1);
		index += size;
		if (fputdword(index, fdout) < 0) return(-1);
	}

	for (ofs = 0; ofs < kp1 -> ofs; ofs++) {
		if (fgetstring(&tmp, fdin) < 0) return(-1);
		if (fputstring(&tmp, fdout) < 0) return(-1);
		free(tmp.kbuf);
		if (fgetstring(&tmp, fdin) < 0) return(-1);
		if (fputstring(&tmp, fdout) < 0) return(-1);
		free(tmp.kbuf);
		if (fgetword(&(tmp.freq), fdin) < 0) return(-1);
		if (fputword(tmp.freq, fdout) < 0) return(-1);
	}
	if (skip) {
		if (fgetstring(&tmp, fdin) < 0) return(-1);
		free(tmp.kbuf);
		if (fgetstring(&tmp, fdin) < 0) return(-1);
		free(tmp.kbuf);
		if (fgetword(&(tmp.freq), fdin) < 0) return(-1);
		ofs++;
	}
	if (fputstring(kp1, fdout) < 0) return(-1);
	if (fputstring(kp2, fdout) < 0) return(-1);
	if (fputword(kp1 -> freq, fdout) < 0) return(-1);
	for (; ofs < ent; ofs++) {
		if (fgetstring(&tmp, fdin) < 0) return(-1);
		if (fputstring(&tmp, fdout) < 0) return(-1);
		free(tmp.kbuf);
		if (fgetstring(&tmp, fdin) < 0) return(-1);
		if (fputstring(&tmp, fdout) < 0) return(-1);
		free(tmp.kbuf);
		if (fgetword(&(tmp.freq), fdin) < 0) return(-1);
		if (fputword(tmp.freq, fdout) < 0) return(-1);
	}

	return(0);
}

VOID saveuserfreq(kana, kbuf)
u_short *kana, *kbuf;
{
	kanjitable tmp1, tmp2;
	lockbuf_t *lck;
	char *cp, path[MAXPATHLEN];
	long argc;
	int n, fdin, fdout;

	if (!kanjilist) return;

	for (argc = 0L; kanjilist[argc].kbuf; argc++)
		if (kbuf == kanjilist[argc].kbuf) break;
	if (!kanjilist[argc].kbuf) return;

	VOID_C openfreqtbl(NULL, 0);
	if (!(lck = openfreqtbl(FREQFILE, O_BINARY | O_RDWR))) return;
	cp = evalpath(strdup2(FREQFILE), 0);
	strcpy(path, cp);
	if ((fdin = lck -> fd) >= 0) fdout = opentmpfile(path, 0666);
	else {
		VOID_C openfreqtbl(NULL, 0);
		lck = lockopen(path,
			O_BINARY | O_WRONLY | O_CREAT | O_EXCL, 0666);
		fdout = (lck) ? lck -> fd : -1;
	}

	if (fdout < 0) {
		VOID_C openfreqtbl(NULL, 0);
		if (fdin < 0) lockclose(lck);
		free(cp);
		return;
	}

	tmp1.kbuf = kana;
	tmp1.klen = kanjilist[argc].match;
	tmp2.kbuf = kbuf;
	tmp2.klen = kanjilist[argc].kmatch;

	n = copyuserfreq(&tmp1, &tmp2, fdin, fdout);
	if (fdin < 0) {
#if	!MSDOS && !defined (CYGWIN)
		if (n < 0) Xunlink(path);
		lockclose(lck);
#else
		lockclose(lck);
		if (n < 0) Xunlink(path);
#endif
	}
	else {
#if	!MSDOS && !defined (CYGWIN)
		if (n >= 0) n = Xrename(path, cp);
		VOID_C openfreqtbl(NULL, 0);
		Xclose(fdout);
#else	/* MSDOS || CYGWIN */
		VOID_C openfreqtbl(NULL, 0);
		Xclose(fdout);
		if (n >= 0) {
# if	MSDOS
			n = Xrename(path, cp);
# else
			while ((n = Xrename(path, cp)) < 0) {
				if (errno != EACCES) break;
				usleep(100000L);
			}
# endif
		}
#endif	/* MSDOS || CYGWIN */
		if (n < 0) Xunlink(path);
	}

	free(cp);
}

static int cmpdict(vp1, vp2)
CONST VOID_P vp1;
CONST VOID_P vp2;
{
	kanjitable *kp1, *kp2;
	u_short *kbuf1, *kbuf2;
	int i, klen1, klen2;

	kp1 = (kanjitable *)vp1;
	kp2 = (kanjitable *)vp2;
	kbuf1 = kp1 -> kbuf;
	kbuf2 = kp2 -> kbuf;
	klen1 = kp1 -> klen;
	klen2 = kp2 -> klen;

	for (i = 0; i < klen1; i++) {
		if (i >= klen2) return(1);
		if (kbuf1[i] > kbuf2[i]) return(1);
		else if (kbuf1[i] < kbuf2[i]) return(-1);
	}

	return(klen1 - klen2);
}

static int cmpfreq(vp1, vp2)
CONST VOID_P vp1;
CONST VOID_P vp2;
{
	kanjitable *kp1, *kp2;

	kp1 = (kanjitable *)vp1;
	kp2 = (kanjitable *)vp2;

	if (kp1 -> len < kp2 -> len) return(1);
	else if (kp1 -> len > kp2 -> len) return(-1);
	if (kp1 -> freq < kp2 -> freq) return(1);
	else if (kp1 -> freq > kp2 -> freq) return(-1);
	if (kp1 -> ofs > kp2 -> ofs) return(1);
	else if (kp1 -> ofs < kp2 -> ofs) return(-1);

	return(0);
}

static long NEAR addkanji(argc, argvp, tmp)
long argc;
kanjitable **argvp, *tmp;
{
	*argvp = (kanjitable *)realloc2(*argvp,
		(argc + 2) * sizeof(kanjitable));
	memcpy((char *)&((*argvp)[argc++]), tmp, sizeof(kanjitable));
	(*argvp)[argc].kbuf = NULL;

	return(argc);
}

static VOID freekanji(argv)
kanjitable *argv;
{
	long n;

	if (argv) {
		for (n = 0L; argv[n].kbuf; n++) free(argv[n].kbuf);
		free(argv);
	}
}

static long NEAR addkanjilist(argc, argvp, argc2, argv2, kp, fd)
long argc;
kanjitable **argvp;
long argc2;
kanjitable *argv2, *kp;
int fd;
{
	kanjitable *next, tmp1, tmp2;
	u_short hinsi[MAXHINSI], shuutan[MAXHINSI];
	long n;
	int i;

	tmp1.kbuf = kp -> kbuf;
	tmp1.klen = kp -> len;
	if (fgetstring(&tmp2, fd) < 0 || fgetword(&(tmp2.freq), fd) < 0
	|| fgethinsi(hinsi, fd) < 0) {
		if (tmp2.kbuf) free(tmp2.kbuf);
		return(argc);
	}

	if (getfreq(&tmp1, &tmp2) <= 0) /*EMPTY*/;
	else if (tmp2.freq < MAXUTYPE(u_short) - freqbias(tmp1.freq))
		tmp2.freq += freqbias(tmp1.freq);
	else tmp2.freq = MAXUTYPE(u_short);
	tmp1.kbuf = tmp2.kbuf;
	tmp1.klen = tmp2.klen;
	tmp1.freq = tmp2.freq;

	tmp2.match = kp -> len;
	tmp2.kmatch = tmp1.klen;
	tmp2.len = kp -> len;
	tmp2.ofs = kp -> ofs;
	if (hinsitblent <= 0) {
		if (kp -> len < kp -> klen) {
			tmp2.klen += kp -> klen - kp -> len;
			tmp2.kbuf = (u_short *)realloc2(tmp2.kbuf,
				(tmp2.klen + 1) * sizeof(u_short));
			memcpy((char *)&(tmp2.kbuf[tmp1.klen]),
				(char *)&(kp -> kbuf[kp -> len]),
				(kp -> klen - kp -> len) * sizeof(short));
			tmp2.kbuf[tmp2.klen] = (short)0;
		}
		return(addkanji(argc, argvp, &tmp2));
	}

	i = fchkhinsi(NULL, kp -> hinsi, hinsi, hinsi, fd);
	if (i >= 0 && kp -> len >= kp -> klen) {
		shuutan[0] = SH_svkantan;
		shuutan[1] = MAXUTYPE(u_short);
		i = fchkhinsi(tmp2.hinsi, hinsi, NULL, shuutan, fd);
		if (i >= 0) return(addkanji(argc, argvp, &tmp2));
	}
	if (i < 0) {
		if (kp -> hinsi[0] == (u_short)HN_SENTOU
		&& kp -> len >= kp -> klen) {
			for (i = 0; i < MAXHINSI; i++) {
				if (hinsi[i] == MAXUTYPE(u_short)) break;
				if (hinsi[i] == (u_short)HN_TANKAN)
					return(addkanji(argc, argvp, &tmp2));
			}
		}
		free(tmp1.kbuf);
		return(argc);
	}

	if (!(next = argv2)) {
		tmp2.kbuf = &(kp -> kbuf[kp -> len]);
		tmp2.klen = kp -> klen - kp -> len;
		memcpy((char *)(tmp2.hinsi), (char *)hinsi, sizeof(hinsi));
		for (tmp2.len = kp -> klen; tmp2.len > (u_char)0; tmp2.len--)
			argc2 = _searchdict(argc2, &argv2, &tmp2, fd);
		if (!argv2) {
			free(tmp1.kbuf);
			return(argc);
		}
	}

	for (n = 0L; n < argc2; n++) {
		if (next
		&& fchkhinsi(tmp2.hinsi, hinsi, NULL, argv2[n].hinsi, fd) < 0)
			continue;

		tmp2.klen = tmp1.klen + argv2[n].klen;
		tmp2.kbuf =
			(u_short *)malloc2((tmp2.klen + 1) * sizeof(u_short));
		memcpy((char *)(tmp2.kbuf), (char *)(tmp1.kbuf),
			tmp1.klen * sizeof(u_short));
		memcpy((char *)&(tmp2.kbuf[tmp1.klen]),
			(char *)(argv2[n].kbuf),
			argv2[n].klen * sizeof(u_short));
		tmp2.kbuf[tmp2.klen] = (u_short)0;

		if (argv2[n].hinsi[0] >= (u_short)HN_MAX) {
			tmp2.len = kp -> len + argv2[n].len;
			tmp2.freq = tmp1.freq + argv2[n].freq;
		}
		else if (tmp2.hinsi[0] >= (u_short)HN_MAX) {
			tmp2.len = argv2[n].len;
			tmp2.freq = argv2[n].freq;
		}
		else if (kp -> len > argv2[n].len) {
			tmp2.len = kp -> len;
			tmp2.freq = tmp1.freq;
		}
		else if (tmp2.len < argv2[n].len) {
			tmp2.len = argv2[n].len;
			tmp2.freq = argv2[n].freq;
		}
		else {
			tmp2.len = kp -> len;
			tmp2.freq = (tmp1.freq > argv2[n].freq)
				? tmp1.freq : argv2[n].freq;
		}

		argc = addkanji(argc, argvp, &tmp2);
	}

	if (!next) freekanji(argv2);
	free(tmp1.kbuf);

	return(argc);
}

static off_t NEAR nextofs(ofs, fd)
off_t ofs;
int fd;
{
	u_char c;

	if (Xlseek(fd, ofs, L_SET) < (off_t)0) return((off_t)-1);
	if (fgetbyte(&c, fd) < 0) return((off_t)-1);
	ofs = (off_t)c * 2 + 2;
	if ((ofs = Xlseek(fd, ofs, L_INCR)) < (off_t)0) return((off_t)-1);
	if (hinsitblent <= 0) return(ofs);
	if (fgetbyte(&c, fd) < 0) return((off_t)-1);
	ofs = (off_t)c * 2;

	return(Xlseek(fd, ofs, L_INCR));
}

static long NEAR _searchdict(argc, argvp, kp, fd)
long argc;
kanjitable **argvp, *kp;
int fd;
{
	kanjitable *argv2, tmp1, tmp2;
	off_t curofs;
	long ofs, min, max, argc2;
	u_char c;
	int n;

	min = -1L;
	max = dicttblent;
	tmp1.kbuf = kp -> kbuf;
	tmp1.klen = kp -> len;
	for (;;) {
		ofs = (min + max) / 2;
		if (ofs <= min || ofs >= max
		|| (fgetjisbuf(&tmp2, ofs, fd)) < 0)
			return(argc);
		n = cmpdict(&tmp1, &tmp2);
		free(tmp2.kbuf);
		if (n > 0) min = ofs;
		else if (n < 0) max = ofs;
		else break;
	}

	kp -> ofs = ofs;
	if (fgetbyte(&c, fd) < 0) return(argc);
	if (c <= 1) return(addkanjilist(argc, argvp, 0, NULL, kp, fd));

	if ((curofs = Xlseek(fd, (off_t)0, L_INCR)) < (off_t)0) return(argc);
	argv2 = NULL;
	argc2 = 0L;
	if (hinsitblent > 0 && kp -> len < kp -> klen) {
		tmp1.kbuf = &(kp -> kbuf[kp -> len]);
		tmp1.klen = kp -> klen - kp -> len;
		tmp1.hinsi[0] = MAXUTYPE(u_short);
		for (tmp1.len = kp -> klen; tmp1.len > (u_char)0; tmp1.len--)
			argc2 = _searchdict(argc2, &argv2, &tmp1, fd);
		if (!argv2) return(argc);
	}

	if (Xlseek(fd, curofs, L_SET) >= (off_t)0) {
		for (n = 0; n < c; n++, curofs = nextofs(curofs, fd)) {
			if (curofs < (off_t)0) break;
			argc = addkanjilist(argc, argvp, argc2, argv2, kp, fd);
		}
	}

	freekanji(argv2);

	return(argc);
}

static long NEAR uniqkanji(argc, argv)
long argc;
kanjitable *argv;
{
	long i, j;
	int c;

	for (i = 0L; i < argc - 1; i++) {
		for (j = 1L; i + j < argc; j++) {
			if (argv[i].klen != argv[i + j].klen) break;
			c = memcmp((char *)(argv[i].kbuf),
				(char *)(argv[i + j].kbuf),
				argv[i].klen * sizeof(u_short));
			if (c) break;
			free(argv[i + j].kbuf);
			if (argv[i + j].len > argv[i].len) {
				argv[i].len = argv[j].len;
				argv[i].freq = argv[j].freq;
			}
			else if (argv[i + j].len == argv[i].len) {
				if (argv[i + j].freq > argv[i].freq)
					argv[i].freq = argv[j].freq;
			}
		}
		if (j <= 1L) continue;
		memmove((char *)&(argv[i + 1]), (char *)&(argv[i + j]),
			(argc + 1 - (i + j)) * sizeof(kanjitable));
		argc -= --j;
	}

	return(argc);
}

VOID freekanjilist(argv)
u_short **argv;
{
	long n, argc;

	if (argv) {
		for (argc = 0L; argv[argc]; argc++) {
			if (kanjilist) {
				for (n = 0L; kanjilist[n].kbuf; n++)
					if (argv[argc] == kanjilist[n].kbuf)
						break;
				if (kanjilist[n].kbuf) continue;
			}
			free(argv[argc]);
		}
		free(argv);
	}
	freekanji(kanjilist);
	kanjilist = NULL;
}

u_short **searchdict(kana, len)
u_short *kana;
int len;
{
	kanjitable tmp;
	u_short **list;
	long n, argc;
	int i, fd;

	freekanjilist(NULL);

	if (!kana) {
		VOID_C opendicttbl(NULL);
		VOID_C openfreqtbl(NULL, 0);
		if (!imebuffer) discarddicttable();
		return(NULL);
	}

	if (len > MAXUTYPE(u_char)) len = MAXUTYPE(u_char);
	argc = 0L;
	if ((fd = opendicttbl(DICTTBL)) < 0) return(NULL);
	readdicttable(fd);
	tmp.kbuf = kana;
	tmp.klen = len;
	tmp.hinsi[0] = (u_short)HN_SENTOU;
	tmp.hinsi[1] = MAXUTYPE(u_short);
	for (tmp.len = len; tmp.len > (u_char)0; tmp.len--)
		argc = _searchdict(argc, &kanjilist, &tmp, fd);

	tmp.klen = len;
	tmp.len = (u_char)0;
	tmp.freq = (u_short)0;
	tmp.hinsi[0] = (u_short)HN_TANKAN;
	tmp.hinsi[1] = MAXUTYPE(u_short);
	tmp.ofs = dicttblent;

	tmp.kbuf = (u_short *)malloc2((len + 1) * sizeof(u_short));
	for (i = 0; i < len; i++) tmp.kbuf[i] = kana[i];
	tmp.kbuf[len] = (u_short)0;
	argc = addkanji(argc, &kanjilist, &tmp);

	tmp.kbuf = (u_short *)malloc2((len + 1) * sizeof(u_short));
	for (i = 0; i < len; i++) {
		if ((kana[i] & 0xff00) != 0x2400) tmp.kbuf[i] = kana[i];
		else tmp.kbuf[i] = (0x2500 | (kana[i] & 0xff));
	}
	tmp.kbuf[len] = (u_short)0;
	tmp.ofs++;
	argc = addkanji(argc, &kanjilist, &tmp);

	if (!argc || !kanjilist) list = NULL;
	else {
		qsort(kanjilist, argc, sizeof(kanjitable), cmpdict);
		argc = uniqkanji(argc, kanjilist);
		qsort(kanjilist, argc, sizeof(kanjitable), cmpfreq);
		list = (u_short **)malloc2((argc + 1) * sizeof(u_short *));
		for (n = 0L; n < argc; n++) list[n] = kanjilist[n].kbuf;
		list[n] = NULL;
	}
	VOID_C openfreqtbl(NULL, 0);

	return(list);
}
#endif	/* !_NOIME */
