#include <ncbi.h>
#include <gish.h>
#include <gishlib.h>
#if defined(YESMFILE) && defined(MMAP_AVAIL)
#include <sys/mman.h>
#ifndef MAP_FILE
#define MAP_FILE	0
#endif
#ifndef MAP_AUTOGROW
#define MAP_AUTOGROW	0
#endif
#ifndef MAP_FAILED
#define MAP_FAILED ((CharPtr)-1)
#endif
#endif /* YESMFILE && MMAP_AVAIL */

#ifdef mfil_open
#undef mfil_open
#undef mfil_close
#undef mfil_read
#undef mfil_write
#undef mfil_seek
#undef mfil_eof
#undef mfil_tell
#undef mfil_getc
#undef mfil_fgetc
#undef mfil_ungetc
#undef mfil_flush
#undef mfil_rewind
#undef mfil_gets
#undef mfil_vgets
#endif


static int	maplen = MFIL_MAPLEN_DEFAULT;

int LIBCALL
mfil_maplen(newlen)
	int	newlen;
{
	int		oldlen;

	oldlen = maplen;
	if (newlen <= 0)
		maplen = MFIL_MAPLEN_DEFAULT;
	else
		maplen = newlen;

	return oldlen;
}

int LIBCALL
mfil_getc(mfp)
	register MFILE	*mfp;
{
#ifdef YESMFILE
	register CharPtr	sp;
	register int	ch;

	sp = MFILE_SP(mfp);
	if (sp != NULL) {
		if (sp < mfp->spend) {
			ch = *sp++;
			mfp->sp = sp;
			return ch;
		}
		return EOF;
	}
	return getc_unlocked(mfp->fp);
#else
	return getc_unlocked(mfp);
#endif
}

int LIBCALL
mfil_ungetc(ch, mfp)
	int	ch;
	MFILE	*mfp;
{
#ifdef YESMFILE
	register CharPtr	sp;

	if ((sp = MFILE_SP(mfp)) != NULL) {
		/* can not push onto a stream that has not been read */
		if (sp == mfp->sp0 || ch == EOF)
			return EOF;
		mfp->sp = sp - 1;
		return ch;
	}
	return ungetc(ch, mfp->fp);
#else
	return ungetc(ch, mfp);
#endif
}

CharPtr LIBCALL
mfil_gets(buf, buflen, mfp)
	register CharPtr	buf;
	size_t	buflen;
	MFILE	*mfp;
{
#ifdef YESMFILE
	CharPtr	buf0 = buf;
	register CharPtr	sp, spend;

	sp = MFILE_SP(mfp);
	if (sp != NULL) {
		spend = mfp->sp + buflen;
		if (mfp->spend < spend)
			spend = mfp->spend;
		if (sp == mfp->spend)
			return NULL;
		spend -= 4; /* this can be problematic with Intel X86 small memory */
		while (sp < spend) {
			if ((*buf++ = *sp++) == '\n')
				break;
			if ((*buf++ = *sp++) == '\n')
				break;
			if ((*buf++ = *sp++) == '\n')
				break;
			if ((*buf++ = *sp++) == '\n')
				break;
		}
		if ((buf > buf0 && buf[-1] != '\n') || sp >= spend) {
			spend += 4;
			while (sp < spend) {
				if ((*buf++ = *sp++) == '\n' || sp == spend)
					break;
				if ((*buf++ = *sp++) == '\n')
					break;
			}
		}
		mfp->sp = sp;
		if (buf - buf0 < buflen)
			*buf = '\0';
		return buf0;
	}
	return fgets(buf, buflen, mfp->fp);
#else
	return fgets(buf, buflen, mfp);
#endif
}

CharPtr LIBCALL
mfil_getsend(buf, buflen, bufend, mfp)
	register CharPtr	buf;
	size_t	buflen;
	CharPtr	PNTR bufend;
	MFILE	*mfp;
{
	register CharPtr	sp;
#ifdef YESMFILE
	CharPtr	buf0 = buf;
	register CharPtr	spend;

	sp = MFILE_SP(mfp);
	if (sp != NULL) {
		spend = mfp->sp + buflen;
		if (mfp->spend < spend)
			spend = mfp->spend;
		if (sp == mfp->spend)
			return NULL;
		spend -= 4; /* this can be problematic with Intel X86 small memory */
		while (sp < spend) {
			if ((*buf++ = *sp++) == '\n')
				break;
			if ((*buf++ = *sp++) == '\n')
				break;
			if ((*buf++ = *sp++) == '\n')
				break;
			if ((*buf++ = *sp++) == '\n')
				break;
		}
		if ((buf > buf0 && buf[-1] != '\n') || sp >= spend) {
			spend += 4;
			while (sp < spend) {
				if ((*buf++ = *sp++) == '\n' || sp == spend)
					break;
				if ((*buf++ = *sp++) == '\n')
					break;
			}
		}
		mfp->sp = sp;
		if (buf - buf0 < buflen)
			*buf = '\0';
		*bufend = buf;
		return buf0;
	}
	sp =  fgets(buf, buflen, mfp->fp);
	if (sp != NULL)
		*bufend = str_chr(buf, '\0');
	return sp;
#else
	sp =  fgets(buf, buflen, mfp);
	if (sp != NULL)
		*bufend = str_chr(buf, '\0');
	return sp;
#endif
}

CharPtr LIBCALL
mfil_xgets(xltab, buf, buflen, bpend, mfp)
	register CharPtr	xltab;
	register CharPtr	buf;
	CharPtr	PNTR bpend;
	size_t	buflen;
	MFILE	*mfp;
{
#ifdef YESMFILE
	CharPtr	buf0 = buf;
	register CharPtr	sp;
	register CharPtr	spend;

	sp = MFILE_SP(mfp);
	if (sp != NULL) {
		spend = mfp->sp + buflen;
		if (mfp->spend < spend)
			spend = mfp->spend;
		if (sp == mfp->spend)
			return NULL;
		spend -= 4; /* this can be problematic with Intel X86 small memory */
		for (;; --buf) {
			while (sp < spend) {
				if ((*buf++ = xltab[*sp++]) == '\0')
					break;
				if ((*buf++ = xltab[*sp++]) == '\0')
					break;
				if ((*buf++ = xltab[*sp++]) == '\0')
					break;
				if ((*buf++ = xltab[*sp++]) == '\0')
					break;
			}
			if (sp >= spend || (buf > buf0 && sp[-1] == '\n'))
				break;
		}
		if (buf > buf0 && sp[-1] != '\n') {
			spend += 4;
			while (sp < spend) {
				if ((*buf++ = xltab[*sp++]) == '\0') {
					--buf;
					if (sp[-1] == '\n')
						break;
				}
			}
			mfp->sp = sp;
			*bpend = buf;
			return buf0;
		}
		else
			if (buf == buf0) {
				spend += 4;
				while (sp < spend) {
					if ((*buf++ = xltab[*sp++]) == '\0') {
						--buf;
						if (sp[-1] == '\n')
							break;
					}
				}
				mfp->sp = sp;
				*bpend = buf;
				return buf0;
			}
		mfp->sp = sp;
		*bpend = buf - 1;
		return buf0;
	}
	return xfgets(xltab, buf, buflen, bpend, mfp->fp);
#else
	return xfgets(xltab, buf, buflen, bpend, mfp);
#endif
}

CharPtr LIBCALL
mfil_xtgets(xltab, tsttab, buf, buflen, bpend, eol, mfp)
	register CharPtr	xltab;
	register UcharPtr	tsttab;
	register CharPtr	buf;
	size_t	buflen;
	CharPtr	PNTR bpend;
	Boolean	PNTR eol;
	MFILE	*mfp;
{
#ifdef YESMFILE
	CharPtr	buf0 = buf;
	register CharPtr	sp;
	register char	ch;
	register CharPtr	spend;

	sp = MFILE_SP(mfp);
	if (sp != NULL) {
		*eol = FALSE;
		spend = mfp->sp + buflen;
		if (sp == mfp->spend) {
			*bpend = buf;
			return NULL;
		}
		if (mfp->spend < spend)
			spend = mfp->spend;
		spend -= 3;
		while (sp < spend) {
			if (tsttab[ch = *sp++]) {
				*buf++ = xltab[ch];
				if (tsttab[ch = *sp++]) {
					*buf++ = xltab[ch];
					if (tsttab[ch = *sp++]) {
						*buf++ = xltab[ch];
						continue;
					}
				}
			}
			if (ch == '\n') {
				*eol = TRUE;
				goto Done;
			}
		}
		spend += 3;
		while (sp < spend) {
			if (tsttab[ch = *sp++]) {
				*buf++ = xltab[ch];
				continue;
			}
			if (ch == '\n') {
				*eol = TRUE;
				goto Done;
			}
		}
		if (sp == mfp->spend)
			*eol = TRUE;
Done:
		mfp->sp = sp;
		*bpend = buf;
		return buf0;
	}
	return fgets(buf, buflen, mfp->fp);
#else
	return fgets(buf, buflen, mfp);
#endif
}

/*
	mfil_vgets -- variable-length mfil_gets

"base" argument points to a pointer to a block of memory obtained from malloc.
"basemax" points to an integer that contains the size of the base block.
"bp" points to a pointer that points to the current buffer location for
        reading within the "base" block.
"mfp" is the MFILE pointer from which to read.
*/
CharPtr LIBCALL
mfil_vgets(base, basemax, bp, mfp)
	CharPtr	PNTR base;
	size_t	PNTR basemax;
	CharPtr	PNTR bp;
	MFILE	*mfp;
{
#ifdef YESMFILE
	register CharPtr	cp;
	size_t	buflen, bpoff, cpoff;

	if (MFILE_SP(mfp) != NULL)
		for (cp = *bp;;) {
			buflen = *basemax - (cp - *base);
			if (buflen > 1) {
				cp[buflen-1] = 'X'; /* lay down sentinel bytes */
				cp[buflen-2] = '\n';
				if (mfil_gets(cp, buflen, mfp) == NULL)
					return (cp == *bp ? NULL : *bp);
				if (cp[buflen-1] != '\0' /* entire buffer was not needed */
						|| cp[buflen-2] == '\n') /* line fit perfectly in buffer */
					return *bp;
			}
			else
				buflen = 1;
			
			bpoff = *bp - *base;
			cpoff = (cp - *base) + buflen - 1;
			if (*base == NULL) {
				if (buflen == 0)
					cp = mem_malloc(buflen = 256);
				else
					cp = mem_malloc(buflen);
			}
			else
				cp = mem_realloc(*base, buflen = *basemax * 1.5 + 10*KBYTE);
			if (cp == NULL)
				return NULL;
			*base = cp;
			*basemax = buflen;
			*bp = cp + bpoff;
			cp += cpoff;
		}
	return vfgets(base, basemax, bp, mfp->fp);
#else
	return vfgets(base, basemax, bp, mfp);
#endif
}

CharPtr LIBCALL
mfil_vgetsend(base, basemax, bp, bpend, mfp)
	CharPtr	PNTR base;
	size_t	PNTR basemax;
	CharPtr	PNTR bp;
	CharPtr	PNTR bpend;
	MFILE	*mfp;
{
#ifdef YESMFILE
	register CharPtr	cp;
	size_t	buflen, bpoff, cpoff;

	cp = *bp;
	
	for (;;) {
		buflen = *basemax - (cp - *base);
		if (buflen > 1) {
			if (mfil_getsend(cp, buflen, bpend, mfp) == NULL)
				return (cp == *bp ? NULL : *bp);
			if (*bpend - cp < buflen || /* entire buffer was not needed */
					(*bpend)[-1] == '\n') /* line fit perfectly in buffer */
				return *bp;
		}
		else
			buflen = 1;
		
		bpoff = *bp - *base;
		cpoff = (cp - *base) + buflen - 1;
		if (*base == NULL) {
			if (buflen == 0)
				cp = mem_malloc(buflen = 256);
			else
				cp = mem_malloc(buflen);
		}
		else
			cp = mem_realloc(*base, buflen = *basemax * 1.5 + 10*KBYTE);
		if (cp == NULL)
			return NULL;
		*base = cp;
		*basemax = buflen;
		*bp = cp + bpoff;
		cp += cpoff;
	}
	/*NOTREACHED*/
#else
	return vfgets(base, basemax, bp, mfp);
#endif
}

CharPtr LIBCALL
mfil_vxgets(xltab, base, basemax, bp, bpend, mfp)
	CharPtr	xltab;
	CharPtr	PNTR base;
	size_t	PNTR basemax;
	CharPtr	PNTR bp;
	CharPtr	PNTR bpend;
	MFILE	*mfp;
{
#ifdef YESMFILE
	register CharPtr	cp;
	size_t	buflen, bpoff, cpoff;

	cp = *bp;
	
	for (;;) {
		buflen = *basemax - (cp - *base);
		if (buflen > 1) {
			cp[buflen-1] = 'X'; /* lay down sentinel bytes */
			cp[buflen-2] = '\n';
			if (mfil_xgets(xltab, cp, buflen, bpend, mfp) == NULL)
				return (cp == *bp ? NULL : *bp);
			if (cp[buflen-1] != '\0') /* entire buffer was not needed */
				return *bp;
		}
		else
			buflen = 1;
		
		bpoff = *bp - *base;
		cpoff = (cp - *base) + buflen - 1;
		if (*base == NULL) {
			if (buflen == 0)
				cp = mem_malloc(buflen = 256);
			else
				cp = mem_malloc(buflen);
		}
		else
			cp = mem_realloc(*base, buflen = *basemax * 1.5 + 10*KBYTE);
		if (cp == NULL)
			return NULL;
		*base = cp;
		*basemax = buflen;
		*bp = cp + bpoff;
		cp += cpoff;
	}
	/*NOTREACHED*/
#else
	return vxfgets(xltab, base, basemax, bp, bpend, mfp);
#endif
}

CharPtr LIBCALL
mfil_vxtgets(xltab, tsttab, base, basemax, bp, bpend, mfp)
	CharPtr	xltab;
	UcharPtr	tsttab;
	CharPtr	PNTR base;
	size_t	PNTR basemax;
	CharPtr	PNTR bp;
	CharPtr	PNTR bpend;
	MFILE	*mfp;
{
#ifdef YESMFILE
	register CharPtr	cp;
	Boolean	eol = FALSE;
	size_t	buflen, bpoff, cpoff, endoff;

	cp = *bp;
	
	for (;;) {
		*bpend = cp;
		buflen = *basemax - (cp - *base);
		if (buflen > 1) {
			if (!eol && mfil_xtgets(xltab, tsttab, cp, buflen, bpend, &eol, mfp) == NULL)
				return (cp == *bp ? NULL : *bp);
			if (eol && *bpend - cp < buflen) /* entire buffer was not needed */
				return *bp;
		}
		else
			buflen = 1;
		
		bpoff = *bp - *base;
		cpoff = (cp - *base) + buflen - 1;
		endoff = *bpend - *base;
		if (*base == NULL) {
			if (buflen == 0)
				cp = mem_malloc(buflen = 256);
			else
				cp = mem_malloc(buflen);
		}
		else
			cp = mem_realloc(*base, buflen = *basemax * 1.5 + 10*KBYTE);
		if (cp == NULL)
			return NULL;
		*base = cp;
		*bpend = cp + endoff;
		*basemax = buflen;
		*bp = cp + bpoff;
		cp += cpoff;
	}
	/*NOTREACHED*/
#else
	/*return vxfgets(base, basemax, bp, mfp); INCORRECT -- NOT DONE */
#endif
}

/*
	mfil_get() -- return a pointer to an area of storage of length 'len'
	that is populated with data from the current MFILE pointer for length
	'len'.  The current MFILE pointer is then advanced by 'len'.

	If MFILE is not in shared memory, then the memory area is allocated
	with malloc() and the data is read into that memory from disk.

	WARNING:  data alignment problems (bus errors) may result from improper
	dereferencing of the pointers returned by this function!
*/
CharPtr LIBCALL
mfil_get(mfp, len)
	MFILE	PNTR mfp;
	size_t	len;
{
	register CharPtr	cp;
#ifdef YESMFILE
	register size_t	diff;

	if (mfp->sp != NULL) {
		diff = mfp->spend - mfp->sp;
		if (diff < len)
			return NULL;
		cp = mfp->sp;
		mfp->sp += len;
		return cp;
	}
#endif

	cp = (CharPtr)mem_malloc(len);
	if (cp == NULL)
		return NULL;
#ifdef YESMFILE
	if (fread(cp, len, 1, mfp->fp) != 1) {
#else
	if (fread(cp, len, 1, mfp) != 1) {
#endif
		mem_free(cp);
		return NULL;
	}
	return cp;
}

size_t
mfil_put_long(mfp, lp, nelem)
	MFILE	PNTR mfp;
	long	PNTR lp;
	size_t	nelem;
{
	register long	PNTR lpmax;

#if BYTE_ORDER == BIG_ENDIAN
	if (sizeof(long) == BO_LONG_SIZE)
		return mfil_write((CharPtr)lp, nelem, BO_LONG_SIZE, mfp);
	lpmax = lp + nelem;
	nelem = 0;
	while (lp < lpmax) {
		nelem += mfil_write(((CharPtr)lp)+(sizeof(long)-BO_LONG_SIZE), 1, BO_LONG_SIZE, mfp);
		++lp;
	}
	return nelem;
#else
	long	l;

	if (sizeof(long) == BO_LONG_SIZE) {
		lpmax = lp + nelem;
		nelem = 0;
		while (lp < lpmax) {
			l = *lp++;
			LONG_BIGENDIAN(l);
			nelem += mfil_write((CharPtr)&l, 1, BO_LONG_SIZE, mfp);
		}
		return nelem;
	}
	lpmax = lp + nelem;
	nelem = 0;
	while (lp < lpmax) {
		l = *lp++;
		LONG_BIGENDIAN(l);
		nelem += mfil_write(((CharPtr)&l)+(sizeof(long)-BO_LONG_SIZE), 1, BO_LONG_SIZE, mfp);
	}
	return nelem;
#endif
}

long PNTR LIBCALL
mfil_get_long(mfp, nelem)
	MFILE	PNTR mfp;
	size_t	nelem;
{
	long	PNTR lp0, PNTR lp, PNTR lpmax, i;
	CharPtr	cp;
	register CharPtr	ip;
	size_t	nbytes;

	nbytes = nelem * BO_LONG_SIZE;

#if BYTE_ORDER == BIG_ENDIAN
	if (sizeof(long) == BO_LONG_SIZE) {
		cp = mfil_get(mfp, nbytes);
		if (cp == NULL)
			return NULL;
		return (long *)cp;
	}
#endif

	lp0 = lp = (long PNTR)mem_malloc(nelem * sizeof(*lp));
	if (lp == NULL)
		return NULL;
	lpmax = lp + nelem;

	if (sizeof(long) == BO_LONG_SIZE) {
#ifdef YESMFILE
		if (mfil_read((CharPtr)lp, nbytes, 1, mfp) != 1) {
#else
		if (fread((CharPtr)lp, nbytes, 1, mfp) != 1) {
#endif
			mem_free(lp0);
			return NULL;
		}
		while (lp < lpmax) {
			LONG_BIGENDIAN(*lp);
			++lp;
		}
		return lp0;
	}


	ip = (CharPtr) &i;
	ip += (sizeof(i) - BO_LONG_SIZE);

#ifdef YESMFILE
	if (mfp->sp != NULL) {
		cp = mfil_get(mfp, nbytes);
		if (cp == NULL) {
			mem_free(lp0);
			return NULL;
		}
		while (lp < lpmax) {
			i = 0;
			memcpy(ip, cp, BO_LONG_SIZE);
			LONG_BIGENDIAN(i);
			*lp++ = i;
			cp += BO_LONG_SIZE;
		}
		return lp0;
	}
#endif

	while (lp < lpmax) {
		i = 0;
#ifdef YESMFILE
		if (fread(ip, BO_LONG_SIZE, 1, mfp->fp) != 1) {
#else
		if (fread(ip, BO_LONG_SIZE, 1, mfp) != 1) {
#endif
			mem_free(lp0);
			return NULL;
		}
		LONG_BIGENDIAN(i);
		*lp++ = i;
	}

	return lp0;
}

/*
	mfil_dup_long -- copy data from the memory file into the provided buffer
	of longs for 'nelem' elements
*/
size_t LIBCALL
mfil_dup_long(mfp, lp, nelem)
	MFILE	PNTR mfp;
	register long	PNTR lp;
	size_t	nelem;
{
	long	i, PNTR lp0 = lp, PNTR lpmax;
	size_t	nbytes, nelem0 = nelem;
	register CharPtr	ip;

	nbytes = nelem * BO_LONG_SIZE;
	lpmax = lp + nelem;

	if (sizeof(long) == BO_LONG_SIZE) {
#ifdef YESMFILE
		if (mfil_read((CharPtr)lp, nbytes, 1, mfp) != 1)
#else
		if (fread((CharPtr)lp, nbytes, 1, mfp) != 1)
#endif
			return 0;
#if BYTE_ORDER != BIG_ENDIAN
		while (lp < lpmax) {
			LONG_BIGENDIAN(*lp);
			++lp;
		}
#endif
		return nelem0;
	}

	ip = (CharPtr) &i;
	ip += (sizeof(i) - BO_LONG_SIZE);

#ifdef YESMFILE
	if (mfp->sp != NULL) {
		register CharPtr	cp;

		cp = mfil_get(mfp, nbytes);
		if (cp == NULL)
			return 0;
		while (lp < lpmax) {
			i = 0;
			Nlm_MemCpy(ip, cp, BO_LONG_SIZE);
			LONG_BIGENDIAN(i);
			*lp++ = i;
			cp += BO_LONG_SIZE;
		}
		return nelem0;
	}
#endif

	while (lp < lpmax) {
		i = 0;
#ifdef YESMFILE
		if (fread(ip, BO_LONG_SIZE, 1, mfp->fp) != 1)
#else
		if (fread(ip, BO_LONG_SIZE, 1, mfp) != 1)
#endif
			return lpmax - lp0;
		LONG_BIGENDIAN(i);
		*lp++ = i;
	}

	return nelem0;
}

MFILE PNTR LIBCALL
mfil_open(name, mode, flags)
	CharPtr	name, mode;
	int		flags;
{
	MFILE	*mfp;
#ifdef YESMFILE
	MFILE	mf;
	int		prot, mflags, fd;
	
	mf.root = NULL;
	mf.fp = NULL;
	mf.shmdp = NULL;
	mf.sp = mf.sp0 = NULL;
	mf.spmax = NULL;
	mf.maplen = 0;
	mf.mode = *mode;
	mf.link_count = 1;

	if (strcmp(name, "-") != 0 && (flags & MFIL_OPT_SHM)) {
		mf.shmdp = shm_attfile(name, mode, FALSE);
		mf.sp0 = mf.sp = (CharPtr)shm_addr(mf.shmdp);
		if (mf.sp != NULL) {
			mf.maplen = mf.filesize = mf.shmdp->filesize;
			mf.spend = mf.spmax = mf.sp + mf.maplen;
			mfp = (MFILE PNTR) mem_dup(&mf, sizeof(mf));
			mfp->root = mfp;
			return mfp;
		}
	}

	mf.fp = openfile(name, mode);
	if (mf.fp == NULL)
		return NULL;

#ifdef MMAP_AVAIL
	if (strcmp(name, "-") != 0 && (flags & MFIL_OPT_MMAP)) {
		switch (*mode) {
		case 'w':
			mflags = MAP_SHARED | MAP_AUTOGROW | MAP_FILE;
			prot = PROT_WRITE;
			break;
		case 'a':
			mflags = MAP_SHARED | MAP_AUTOGROW | MAP_FILE;
			prot = PROT_READ | PROT_WRITE;
			break;
		case 'r':
		default:
			mflags = MAP_SHARED | MAP_FILE;
			prot = PROT_READ;
		}
		fd = fileno(mf.fp);
		if (flags & MFIL_OPT_LOCK) {
			switch (*mode) {
			case 'r':
				SYS_READWLOCK(fd, 0, SEEK_SET, 0);
				break;
			case 'w':
			case 'a':
				SYS_WRITEWLOCK(fd, 0, SEEK_SET, 0);
				break;
			default:
				break;
			}
		}
		mf.filesize = sys_filesize(name);
		switch (*mode) {
		case 'r':
			mf.maplen = mf.filesize;
			break;
		case 'w':
		case 'a':
			mf.maplen = maplen;
			if (*mode == 'w')
				mf.filesize = 0;
		default:
			break;
		}
		mf.sp0 = mf.sp = mmap(NULL, mf.maplen, prot, mflags, fd, 0);
		if (mf.sp == (CharPtr)MAP_FAILED) {
			mf.sp0 = mf.sp = NULL;
		}
		mf.spend = mf.sp0 + mf.filesize;
		/* Leave the file open if write-accessible */
		if (mf.sp != NULL && *mode == 'r' && !(flags & MFIL_OPT_LOCK)) {
			fclose(mf.fp);
			mf.fp = NULL;
		}
		if (*mode == 'r')
			mf.spmax = mf.sp + mf.filesize;
		if (*mode == 'w' || *mode == 'a')
			mf.spmax = mf.sp + mf.maplen;
	}
#endif /* MMAP_AVAIL */

	mfp = (MFILE PNTR) mem_dup(&mf, sizeof(mf));
	mfp->root = mfp;
	return mfp;

#else /* !YESMFILE */

	mfp = openfile(name, mode);
	if (mfp != NULL && (flags & MFIL_OPT_LOCK)) {
		int	fd;
		fd = fileno(mfp);
		switch (*mode) {
		case 'r':
			SYS_READWLOCK(fd, 0, SEEK_SET, 0);
			break;
		case 'w':
		case 'a':
			SYS_WRITEWLOCK(fd, 0, SEEK_SET, 0);
			break;
		default:
			break;
		}
	}

	return mfp;

#endif /* !YESMFILE */
}

MFILE *
mfil_link(mfp, fname, mode)
	MFILE	*mfp;
	CharPtr	fname;
	CharPtr	mode;
{
#ifdef YESMFILE
	MFILE	*newfp, *root;

	mproc_lock();
	if (mfp == NULL || (mfp->fp == NULL && mfp->sp == NULL)) {
		mproc_unlock();
		return NULL;
	}
	if (mfp->sp == NULL) {
		newfp = mem_dup((VoidPtr)mfp, sizeof(*mfp));
		mproc_unlock();
		if (newfp == NULL)
			return NULL;
		newfp->root = NULL;
		newfp->link_count = 1;
		newfp->fp = fopen(fname, mode);
		if (newfp->fp != NULL)
			return newfp;
		mem_free(newfp);
		return NULL;
	}
	root = mfp->root;
	newfp = mem_dup((VoidPtr)root, sizeof(*root));
	if (newfp == NULL)
		goto Exit;
	newfp->link_count = 1;
	newfp->root = root;
	++root->link_count;
Exit:
	mproc_unlock();
	return newfp;
#else /* !YESMFILE */
	if (mfp == NULL)
		return NULL;
	return fopen(fname, mode);
#endif /* !YESMFILE */
}

int LIBCALL
mfil_inmemory(mfp)
	MFILE	PNTR mfp;
{
#ifdef YESMFILE
	return mfp->sp != NULL && mfp->shmdp != NULL;
#else
	return 0;
#endif
}

int LIBCALL
mfil_close(mfp)
	MFILE	PNTR mfp;
{
#ifdef YESMFILE
	MFILE	*root;

	if (mfp == NULL)
		return -1;
	/* file pointers are maintained separately, not like a link would be */
	if (mfp->sp == NULL && mfp->fp != NULL) {
		fclose(mfp->fp);
		Nlm_MemSet((VoidPtr)mfp, 0, sizeof(*mfp));
		mem_free(mfp);
		return 0;
	}
	mproc_lock();
	root = mfp->root;
	if (root != mfp && root != NULL) {
		--root->link_count;
		mproc_unlock();
		Nlm_MemSet((VoidPtr)mfp, 0, sizeof(*mfp));
		mem_free(mfp);
		return 0;
	}
	if (--mfp->link_count > 0) {
		mproc_unlock();
		return 0;
	}
	if (mfp->sp != NULL) {
		if (mfp->shmdp != NULL)
			(void) shm_dropfile(mfp->shmdp);
		if (mfp->fp != NULL)
			fclose(mfp->fp);
#ifdef MMAP_AVAIL
		else {
			munmap(mfp->sp0, mfp->maplen);
			if (mfp->fp != NULL)
				ftruncate(fileno(mfp->fp), mfp->spend - mfp->sp0);
		}
#endif /* MMAP_AVAIL */
	}
	Nlm_MemSet((Nlm_VoidPtr)mfp, 0, sizeof(*mfp));
	mproc_unlock();
	mem_free(mfp);
	return 0;
#else
	return fclose(mfp);
#endif
}

void LIBCALL
mfil_free(mfp, cp)
	MFILE	PNTR mfp;
	CharPtr	cp;
{
#ifdef YESMFILE
	if (mfp->sp != NULL)
		return;
	if (cp >= mfp->sp0 && cp < mfp->spend)
		return;
#endif
	mem_free(cp);
}

long int LIBCALL
mfil_tell(mfp)
	MFILE	PNTR mfp;
{
#ifdef YESMFILE
	if (mfp->sp != NULL)
		return mfp->sp - mfp->sp0;
	return ftell(mfp->fp);
#else
	return ftell(mfp);
#endif
}

int LIBCALL
mfil_eof(mfp)
	MFILE	PNTR mfp;
{
#ifdef YESMFILE
	if (mfp->sp != NULL) {
		if (mfp->sp < mfp->spend)
			return 0;
		return 1;
	}
	return feof(mfp->fp);
#else
	return feof(mfp);
#endif
}

void LIBCALL
mfil_prefetch(mfp)
	MFILE	PNTR mfp;
{ /* pre-read the file into memory, forcing system to assign VM pages */
#if defined(YESMFILE) && defined(MPROC_AVAIL)
	register CharPtr	cp, cpmax;
	register int	ch = 0;

	if (mfp->sp == NULL)
		return;

	for (cp = mfp->sp, cpmax = mfp->sp + mfp->filesize; cp < cpmax; cp += 512)
		ch += *cp;
#else
	return;
#endif
}

/* mfil_read -- analogous to ANSI C fread() */
int LIBCALL
mfil_read(buf, size, nitems, mfp)
	register CharPtr	buf;
	size_t	size, nitems;
	MFILE	PNTR mfp;
{
#ifdef YESMFILE
	register size_t	diff, len;
	register CharPtr	cp, cpmax;

	if (mfp->sp != NULL) {
		len = size * nitems;
		diff = mfp->spend - mfp->sp;
		if (len > diff) {
			nitems = diff / size;
			len = nitems * size;
		}
		cp = mfp->sp;
		mfp->sp = cpmax = cp + len;
		while (cp < cpmax) {
			*buf++ = *cp++;
		}
		return nitems;
	}
	return fread(buf, size, nitems, mfp->fp);
#else
	return fread(buf, size, nitems, mfp);
#endif
}

/* mfil_write -- analog to ANSI C fwrite() */
int LIBCALL
mfil_write(buf, size, nitems, mfp)
	register CharPtr	buf;
	size_t	size, nitems;
	MFILE	PNTR mfp;
{
#ifdef YESMFILE
	register size_t	len;
	size_t	diff;

	if (mfp->sp != NULL) {
		len = size * nitems;
		diff = mfp->spmax - mfp->sp;
		if (diff < len) {
			nitems = diff / size;
			len = nitems * size;
		}
		Nlm_MemCpy(mfp->sp, buf, len);
		mfp->sp += len;
		if (mfp->sp > mfp->spend)
			mfp->spend = mfp->sp;
		return nitems;
	}
	return fwrite(buf, size, nitems, mfp->fp);
#else
	return fwrite(buf, size, nitems, mfp);
#endif
}

int LIBCALL
mfil_flush(mfp)
	MFILE	*mfp;
{
#ifdef YESMFILE
	if (mfp == NULL) {
		return fflush(NULL);
	}
	if (mfp->fp != NULL && (mfp->mode == 'w' || mfp->mode == 'a'))
		return fflush(mfp->fp);
	return 0;
#else
	return fflush(mfp);
#endif
}

void
mfil_rewind(mfp)
	MFILE	*mfp;
{
#ifdef YESMFILE
	if (MFILE_SP(mfp) != NULL) {
		mfp->sp = mfp->sp0;
		return;
	}
	rewind(mfp->fp);
	return;
#else
	rewind(mfp);
	return;
#endif
}

/*
	mfil_seek() -- reposition the memory file pointer
*/
int LIBCALL
mfil_seek(mfp, offset, ptrname)
	MFILE	PNTR mfp;
	long	offset;
	int		ptrname;
{
#ifdef YESMFILE
	register CharPtr	cp;

	if (mfp->sp != NULL) {
		switch (ptrname) {
			case SEEK_SET: /* relative to beginning */
				cp = mfp->sp0 + offset;
				if (offset < 0 || cp >= mfp->spend)
					return -1;
				mfp->sp = cp;
				break;
			case SEEK_CUR: /* relative to current position */
				cp = mfp->sp + offset;
				if (cp >= mfp->spend || cp < mfp->sp0)
					return -1;
				mfp->sp = cp;
				break;
			case SEEK_END: /* relative to end of file */
				if (offset > 0 || mfp->filesize < -offset)
					return -1;
				mfp->sp = mfp->sp0 + (mfp->filesize + offset);
				break;
			default:
				return -1;
		}
		return 0;
	}
	return fseek(mfp->fp, offset, ptrname);
#else
	return fseek(mfp, offset, ptrname);
#endif
}

int LIBCALL
mfil_unlock(mfp)
	MFILE	PNTR mfp;
{
	int	fd;
#ifdef YESMFILE
	if (mfp->fp == NULL)
		return -1;
	fd = fileno(mfp->fp);
#else
	fd = fileno(mfp);
#endif
	return SYS_UNLOCK(fd, 0, SEEK_SET, 0);
}
