/*
 *	Copyright 1993, University Corporation for Atmospheric Research
 *      See netcdf/COPYRIGHT file for copying and redistribution conditions.
 */
/*	$Id: file.c,v 1.74 1996/02/15 21:47:19 steve Exp $ */

#include	<string.h>

#ifdef vms
#   include <unixlib.h>
#endif

#include	"local_nc.h"
#include	"alloc.h"

static int _ncdf = 0 ; /*  high water mark on open cdf's */
static NC *_cdfs[MAX_NC_OPEN] ;
#define HNDLE(id) (((id) >= 0 && (id) < _ncdf) ? _cdfs[(id)] : NULL)
#define STASH(id) (((id) >= 0 && (id) < _ncdf) ?  \
		HNDLE(_cdfs[(id)]->redefid) : NULL)

#ifdef NO_STDC_REMOVE
/* try unix 'unlink' */
#define remove(fpath) unlink((fpath))
#endif

#ifdef DOS_FS
#define SEP '\\' /* this separates path components on DOS */
#endif
#ifndef SEP
#define SEP '/' /* default, unix */
#endif

/*
 *  Check validity of cdf handle, return pointer to NC struct or
 * NULL on error.
 */
NC *
NC_check_id(cdfid)
int cdfid ;
{
	NC *handle ;
	
	handle = ( cdfid >= 0 && cdfid < _ncdf) ? _cdfs[cdfid] : NULL ;
	if(handle == NULL)
	{
		NCadvise(NC_EBADID, "%d is not a valid cdfid", cdfid) ;
		return(NULL) ;
	}
	return(handle) ;
}


/*
 *  Check to see if in define mode.
 * If 'iserr' arg is true, advise.
 */
bool_t
NC_indefine(cdfid, iserr) /* Should be a Macro ? */
int cdfid ;
bool_t iserr ;
{
	bool_t ret  ;
	ret = (cdfid >= 0 && cdfid < _ncdf) ?
		(bool_t)(_cdfs[cdfid]->flags & NC_INDEF) : FALSE ;
	if(!ret && iserr)
	{
		if(cdfid < 0 || cdfid >= _ncdf)
			NCadvise(NC_EBADID, "%d is not a valid cdfid", cdfid) ;
		else
			NCadvise(NC_ENOTINDEFINE, "%s Not in define mode",
				_cdfs[cdfid]->path) ;
	}
	return(ret) ;
}


/*
 *  Common code for ncopen and nccreate.
 */
static int
NC_open(path, mode )
const char	*path ;	/* file name */
int mode ;
{
	NC *handle ;
	int id ;

	/* find first available id */
	for(id = 0 ; id < _ncdf; id++)
		if( _cdfs[id] == NULL) break ;

	if(id == _ncdf && _ncdf >= MAX_NC_OPEN) /* will need a new one */
	{
		NCadvise(NC_ENFILE, "maximum number of open cdfs %d exceeded",
			 _ncdf) ;
		return(-1) ;
	}

	handle = NC_new_cdf(path, mode) ;
	if( handle == NULL)
	{
		if((mode & 0x0f) == NC_CLOBBER)
		{
			if( remove(path) != 0 )
				nc_serror("couldn't remove filename \"%s\"", path) ;
		}
		return(-1) ;
	}

	(void) strncpy(handle->path, path, NC_FILENAME_MAX) ;
	_cdfs[id] = handle ;
	if(id == _ncdf)
		_ncdf++ ;
	return(id) ;
}


nccreate(path, cmode)
const char	*path ;	/* file name */
int 		cmode ;
{
	cdf_routine_name = "nccreate" ;

	if(cmode & NC_CREAT)
	{
		return(NC_open(path, cmode)) ;
	}
	NCadvise(NC_EINVAL, "Bad Flag") ;
	return(-1) ;
}


ncopen(path,mode)
const char	*path ;	/* file name */
int 		mode ;
{
	cdf_routine_name = "ncopen" ;
	if(mode & NC_CREAT)
	{
		NCadvise(NC_EINVAL, "Bad Flag") ;
		return(-1) ;
	}
	return(NC_open(path, mode)) ;
}


ncsync(id)
int id ;
{
	NC *handle ;
	int reRead = 1; /* flag to read the header */

	cdf_routine_name = "ncsync" ;

	handle = NC_check_id(id) ; 
	if(handle == NULL)
		return(-1) ;

	if( handle->flags & NC_INDEF )
	{
		NCadvise(NC_EINDEFINE, "Unfinished definition") ;
		return(-1) ;
	}

	if(handle->flags & NC_RDWR)
	{
		handle->xdrs->x_op = XDR_ENCODE ;
		if(handle->flags & NC_HDIRTY)
		{
			if(!xdr_cdf(handle->xdrs, &handle) )
				/* MEMORY: should xdr_free(xdr_cdf, &handle) be
				 * called? */
				return(-1) ;
			handle->flags &= ~(NC_NDIRTY | NC_HDIRTY) ;
			reRead = 0;
		}
		else if(handle->flags & NC_NDIRTY)
		{
			if(!xdr_numrecs(handle->xdrs, handle) )
				/* MEMORY: should xdr_free(xdr_numrecs,
				 * handle) be called? */
				return(-1) ;
			handle->flags &= ~(NC_NDIRTY) ;
		}
	}

	(void) NCxdrfile_sync(handle->xdrs) ;

	if(reRead != 0)
	{
		/* free the stuff in handle that xdr_cdf allocates */
		handle->xdrs->x_op = XDR_FREE ;
		(void) xdr_cdf(handle->xdrs, &handle) ;

		/* reread the header */
		handle->xdrs->x_op = XDR_DECODE ;
		if(!xdr_cdf(handle->xdrs, &handle) )
		{
			nc_serror("xdr_cdf") ;
			/* MEMORY: should xdr_free(xdr_cdf,
			 * &handle) be called? */
			NC_free_cdf(handle) ; /* ?? what should we do now? */
			return(-1) ;
		}
		if( NC_computeshapes(handle) == -1)
			return(-1) ;
	}

	return(0) ;
}


/*
 *  In data mode, same as ncclose ;
 * In define mode, restore previous definition ;
 * In create, remove the file ;
 */
ncabort(cdfid)
int cdfid ;
{
	NC *handle ;
	char path[NC_FILENAME_MAX + 1] ;
	unsigned flags ;

	cdf_routine_name = "ncabort" ;


	handle = NC_check_id(cdfid) ; 
	if(handle == NULL)
		return(-1) ;

	flags = handle->flags ; /* need to save past free_cdf */

	/* NC_CREAT implies NC_INDEF, in both cases need to remove handle->path */
	if(flags & (NC_INDEF | NC_CREAT))
	{
		(void)strncpy(path, handle->path, NC_FILENAME_MAX) ; /* stash path */
		if(!(flags & NC_CREAT)) /* redef */
		{
			NC_free_cdf(STASH(cdfid)) ;

			_cdfs[handle->redefid] = NULL ;
			if(handle->redefid == _ncdf - 1)
				_ncdf-- ;
			handle->redefid = -1 ;
		}
	}
	else if(handle->flags & NC_RDWR)
	{
		handle->xdrs->x_op = XDR_ENCODE ;
		if(handle->flags & NC_HDIRTY)
		{
			if(!xdr_cdf(handle->xdrs, &handle) )
				/* MEMORY: should xdr_free(xdr_cdf,
				 * &handle) be called? */
				return(-1) ;
		}
		else if(handle->flags & NC_NDIRTY)
		{
			if(!xdr_numrecs(handle->xdrs, handle) )
				/* MEMORY: should xdr_free(xdr_numrecs,
				 * handle) be called? */
				return(-1) ;
		}
	}

	NC_free_cdf(handle) ; /* calls close */

	if(flags & (NC_INDEF | NC_CREAT))
	{
		if( remove(path) != 0 )
			nc_serror("couldn't remove filename \"%s\"", path) ;
	}

	_cdfs[cdfid] = NULL ;

	if(cdfid == _ncdf - 1)
		_ncdf-- ;

	return(0) ;
}


/* 
 * Deprecated function ;
 */
ncnobuf(cdfid)
int cdfid ;
{
	NC *handle ;

	cdf_routine_name = "ncnobuf" ;

	handle = NC_check_id(cdfid) ; 
	if(handle == NULL)
		return(-1) ;
/* NOOP */
	return(0);
}


/*
 * Given the path to a file "proto",
 * we replace the filename component with
 * a name like one would get from tmpnam(3S).
 * (Many implementations of tmpnam insist on giving us
 * a directory like /usr/tmp as well. Since we are making a copy which we
 * will eventually rename() back to proto, we want the return of NCtempname
 * and proto to dwell on the same filesystem.)
 */
static char *
NCtempname(proto)
const char *proto ;
{
/* NO_ACCESS defined if the OS lacks the access() function */
#ifndef NO_ACCESS
#	define TN_NACCES 1
#else 
#	define TN_NACCES 0
#endif /* !NO_ACCESS */
/* NO_GETPID defined if the OS lacks the getpid() function */
#ifndef NO_GETPID
#	define TN_NDIGITS 4
	unsigned int pid ; /* OS/2 DOS (MicroSoft Lib) allows "negative" int pids */
#else
#	define TN_NDIGITS 0
#endif /* !NO_GETPID */

	static char seed[] = {'a','a','a', '\0'} ;
#define TN_NSEED (sizeof(seed) -1)
	static char tnbuf[NC_FILENAME_MAX +1] ;
	char *begin, *cp, *sp ;

	/* assert(TN_NSEED > 0) ; */
	(void) strncpy(tnbuf, proto, NC_FILENAME_MAX) ;

#ifdef SEP
	if ((begin = strrchr(tnbuf, SEP)) == NULL)
		begin = tnbuf ;
	else
	    begin++ ;

	if(&tnbuf[NC_FILENAME_MAX] - begin <= TN_NSEED + TN_NACCES + TN_NDIGITS)
	{
		/* not big enough */
		tnbuf[0] = '\0' ;
		return tnbuf ;
	}
#else
	begin = tnbuf ;
#endif /* SEP */

	*begin = '\0' ;
	(void) strcat(begin, seed) ;

	cp = begin + TN_NSEED + TN_NACCES + TN_NDIGITS ;
#ifndef NO_GETPID
	*cp = '\0' ;
	pid = getpid() ;
	while(--cp >= begin + TN_NSEED + TN_NACCES)
	{
		*cp = (pid % 10) + '0' ;
		pid /= 10 ;
	}
#else
	*cp-- = '\0' ;
#endif /* !NO_GETPID */

	/* update seed for next call */
	sp = seed ;
	while(*sp == 'z')
		*sp++ = 'a' ;
	if(*sp != '\0')
		++*sp ;

#ifndef NO_ACCESS
	for(*cp = 'a' ; access(tnbuf, 0) == 0 ; )
	{
		if(++*cp > 'z')
		{
			/* ran out of tries */
			tnbuf[0] = '\0' ;
			return tnbuf ;
		}
	}
#endif /* !NO_ACCESS */

	return tnbuf ;
}


ncredef(cdfid)
int cdfid ;
{
	NC *handle ;
	NC *new ;
	int id ;
	char *scratchfile ;

	cdf_routine_name = "ncredef" ;

	handle = NC_check_id(cdfid) ; 
	if(handle == NULL)
		return(-1) ;
	if( handle->flags & NC_INDEF) /* in define mode already */
	{
		NC *stash = STASH(cdfid) ;
		NCadvise(NC_EINDEFINE, "%s: in define mode aleady",
			 stash ? stash->path : handle->path) ;

		return(-1) ;
	}
	if(!(handle->flags & NC_RDWR))
	{
		NCadvise(NC_EPERM, "%s: NC_NOWRITE", handle->path) ;
		return(-1) ;
	}

	if(handle->flags & NC_HDIRTY || handle->flags & NC_NDIRTY)
	{
		handle->xdrs->x_op = XDR_ENCODE ;
		if(!xdr_cdf(handle->xdrs, &handle) )
			/* MEMORY: should xdr_free(xdr_cdf, &handle) be
			 * called? */
			return(-1) ;
		handle->flags &= ~(NC_NDIRTY | NC_HDIRTY) ;
	}

	/* find first available id */
	for(id = 0 ; id < _ncdf; id++)
		if( _cdfs[id] == NULL) break ;

	if(id == _ncdf && _ncdf >= MAX_NC_OPEN) /* will need a new one */
	{
		NCadvise(NC_ENFILE, "maximum number of open cdfs %d exceeded",
			 _ncdf) ;
		return(-1) ;
	}

	if( ncopts & NC_NOFILL )
	{
		/* fill last record */
		handle->xdrs->x_op = XDR_ENCODE ;
		if(handle->flags & NC_NDIRTY)
		{
			if(!xdr_numrecs(handle->xdrs, handle) )
				/* MEMORY: should xdr_free(xdr_numrecs,
				 * handle) be called? */
				return(-1) ;
			handle->flags &= ~(NC_NDIRTY) ;
		}
	}

	scratchfile = NCtempname(handle->path) ;

	new = NC_dup_cdf(scratchfile, NC_NOCLOBBER, handle) ;
	if(new == NULL)
	{
		return(-1) ;
	}

	handle->flags |= NC_INDEF ;
	(void) strncpy(new->path, scratchfile, NC_FILENAME_MAX) ;

	/* put the old handle in the new id */
	_cdfs[id] = handle ;
	if(id == _ncdf)
		_ncdf++ ;

	/* put the new handle in old id */
	_cdfs[cdfid] = new ;

	new->redefid = id ;

	return(0) ;
}


/*
 * Compute offsets and put into the header
 */
static void
NC_begins(handle)
NC *handle ;
{
	int ii ;
	u_long index = 0 ;
	NC_var **vpp ;
	NC_var *last = NULL ;

	if(handle->vars == NULL) 
		return ;

	index = NC_xlen_cdf(handle) ;

	/* loop thru vars, first pass is for the 'non-record' vars */
	vpp = (NC_var **)handle->vars->values ;
	for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
	{
		if( IS_RECVAR(*vpp) )
		{
			continue ;	/* skip record variables on this pass */
		}

		(*vpp)->begin = index ;
		index += (*vpp)->len ;
#ifdef EDEBUG
		NCadvise(NC_NOERR, "%s pass 1 begin %d, length %d",
			(*vpp)->name->values, (*vpp)->begin, (*vpp)->len) ;
#endif /* EDEBUG */
	}

	handle->begin_rec = index ;
	handle->recsize = 0 ;

	/* loop thru vars, second pass is for the 'non-record' vars */
	vpp = (NC_var **)handle->vars->values ;
	for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
	{
		if( !IS_RECVAR(*vpp) )
		{
			continue ;	/* skip non-record variables on this pass */
		}

		(*vpp)->begin = index ;
#ifdef EDEBUG
		NCadvise(NC_NOERR, "%s pass 2 begin %d, len %d, *dsizes %d",
			(*vpp)->name->values, (*vpp)->begin, index, (*vpp)->len, *(*vpp)->dsizes ) ;
#endif /* EDEBUG */
		index += (*vpp)->len ;
		handle->recsize += (*vpp)->len ;
		last = (*vpp) ;
	}
	/*
	 * for special case of exactly one record variable, pack values
	 */
	if(last != NULL && handle->recsize == last->len)
		handle->recsize = *last->dsizes ;
	handle->numrecs = 0 ;
}


/*
 * Copy nbytes bytes from source to target.
 * Streams target and source should be positioned before the call.
 * opaque I/O, no XDR conversion performed (or needed).
 * The Macros XDR_GETBYTES and XDR_PUTBYTES may not be
 * supported on your xdr implementation. If not, calls
 * to xdr_opaque may be used.
 */
bool_t
NC_dcpy( target, source, nbytes)
XDR *target ;
XDR *source ;
long nbytes ;
{
/* you may wish to tune this: big on a cray, small on a PC? */
#define NC_DCP_BUFSIZE 8192
	char buf[NC_DCP_BUFSIZE] ;

	while(nbytes > sizeof(buf))
	{
		if(!XDR_GETBYTES(source, buf, sizeof(buf)))
			goto err ;
		if(!XDR_PUTBYTES(target, buf, sizeof(buf)))
			goto err ;
		nbytes -= sizeof(buf) ;
	}
	/* we know nbytes <= sizeof(buf) at this point */
	if(!XDR_GETBYTES(source, buf, nbytes))
		goto err ;
	if(!XDR_PUTBYTES(target, buf, nbytes))
		goto err ;
	return(TRUE) ;
err:
	NCadvise(NC_EXDR, "NC_dcpy") ;
	return(FALSE) ;
}


/*
 * XDR the data of varid in old, target is the new xdr strm
 */
static bool_t
NC_vcpy( target, old, varid)
XDR *target ;
NC *old ;
int varid ;
{
	NC_var **vpp ;
	vpp = (NC_var **)old->vars->values ;
	vpp += varid ;

	if( !xdr_setpos(old->xdrs, (*vpp)->begin) )
	{
		NCadvise(NC_EXDR, "NC_vcpy: xdr_setpos") ;
		return(FALSE) ;
	}
		
	return(NC_dcpy(target, old->xdrs, (*vpp)->len)) ;
}


/*
 * XDR the data of (varid, recnum) in old, target is the new xdr strm
 */
static bool_t
NC_reccpy( target, old, varid, recnum)
XDR *target ;
NC *old ;
int varid ;
int recnum ;
{
	NC_var **vpp ;
	vpp = (NC_var **)old->vars->values ;
	vpp += varid ;

	if( !xdr_setpos(old->xdrs,
			(*vpp)->begin + old->recsize*recnum )){
		NCadvise(NC_EXDR, "NC_reccpy: xdr_setpos") ;
		return(FALSE) ;
	}
	
	return(NC_dcpy(target, old->xdrs, (*vpp)->len)) ;
}


/*
 *  Common code for ncendef, ncclose(endef)
 */
static
NC_endef( cdfid, handle )
int cdfid ;
NC *handle ;
{
	XDR *xdrs ;
	int ii ;
	int jj = 0 ;
	NC_var **vpp ;
	NC *stash = STASH(cdfid) ; /* faster rvalue */

	NC_begins(handle) ;

	xdrs = handle->xdrs ;
	xdrs->x_op = XDR_ENCODE ;

	if(!xdr_cdf(xdrs, &handle) )
	{
		nc_serror("xdr_cdf") ;
		/* MEMORY: should xdr_free(xdr_cdf, &handle) be called? */
		return(-1) ;
	}

	if(handle->vars == NULL) 
		goto done ;
	
	/* loop thru vars, first pass is for the 'non-record' vars */
	vpp = (NC_var **)handle->vars->values ;
	for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
	{
		if( IS_RECVAR(*vpp) )
		{
			continue ;	/* skip record variables on this pass */
		}

		if( !(handle->flags & NC_CREAT) && stash->vars != NULL
			&& ii < stash->vars->count)
		{
			/* copy data */
			if( !NC_vcpy(xdrs, stash, ii) )
				return(-1) ;
			continue ;
		} /* else */

		if( !(handle->flags & NC_NOFILL) )
			if( !xdr_NC_fill(xdrs, *vpp) )
				/* MEMORY: should xdr_free(xdr_NC_fill,
				 * &vpp) be called? */
				return(-1) ;
	}

	if(!(handle->flags & NC_CREAT)) /* after redefinition */
	{
		for(jj = 0 ; jj < stash->numrecs ; jj++)
		{
			vpp = (NC_var **)handle->vars->values ;
			for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
			{
				if( !IS_RECVAR(*vpp) )
				{
					continue ;	/* skip non-record variables on this pass */
				}
				if( stash->vars != NULL && ii < stash->vars->count)
				{
					/* copy data */
					if( !NC_reccpy(xdrs, stash, ii, jj) )
						return(-1) ;
					continue ;
				} /* else */
				if( !(handle->flags & NC_NOFILL) )
					if( !xdr_NC_fill(xdrs, *vpp) )
						/* MEMORY: should 
						 * xdr_free(xdr_NC_fill,
						 * *vpp) be called? */
						return(-1) ;
			}
		}
		handle->numrecs = stash->numrecs ;
		if(!xdr_numrecs(handle->xdrs, handle) )
			/* MEMORY: should xdr_free(xdr_numrecs,
			 * handle) be called? */
			return(-1) ;
	}

#ifdef EDEBUG
	NCadvise(NC_NOERR, "begin %d, recsize %d, numrecs %d",
		handle->begin_rec, handle->recsize, handle->numrecs) ;
#endif /* EDEBUG */

	if(!(handle->flags & NC_CREAT)) /* redefine */
	{
		char realpath[NC_FILENAME_MAX + 1] ;
		strcpy(realpath, stash->path) ;

		/* close stash */
		NC_free_cdf(stash) ;
#ifdef DOS_FS
		xdr_destroy(handle->xdrs) ; /* close handle */
		if( remove(realpath) != 0 )
			nc_serror("couldn't remove filename \"%s\"", realpath) ;
#endif
		if( rename(handle->path, realpath) != 0)
		{
			nc_serror("rename %s -> %s failed", handle->path, realpath) ;
			/* try to restore state prior to redef */
			_cdfs[cdfid] = stash ;
			_cdfs[handle->redefid] = NULL ;
			if(handle->redefid == _ncdf - 1)
				_ncdf-- ;
			NC_free_cdf(handle) ;
			return(-1) ;
		}
		(void) strncpy(handle->path, realpath, NC_FILENAME_MAX) ;
#ifdef DOS_FS
		if( NCxdrfile_create( handle->xdrs, handle->path, NC_WRITE ) < 0) 
			return -1 ;
#endif
		_cdfs[handle->redefid] = NULL ;
		if(handle->redefid == _ncdf - 1)
			_ncdf-- ;
		handle->redefid = -1 ;
	}

done:
	handle->flags &= ~(NC_CREAT | NC_INDEF | NC_NDIRTY | NC_HDIRTY) ;
	return(0) ;
}


ncendef( cdfid )
int cdfid ;
{
	NC *handle ;

	cdf_routine_name = "ncendef" ;

	handle = NC_check_id(cdfid) ; 
	if(handle == NULL)
		return(-1) ;
	if( !NC_indefine(cdfid,TRUE) )
		return(-1) ;
	return( NC_endef(cdfid, handle) ) ;
}


ncclose( cdfid )
int cdfid ;
{
	NC *handle ;

	cdf_routine_name = "ncclose" ;

	handle = NC_check_id(cdfid) ; 
	if(handle == NULL)
		return(-1) ;

	if( handle->flags & NC_INDEF)
	{
		if( NC_endef(cdfid, handle) == -1 )
		{
			return( ncabort(cdfid) ) ;
		}
	}
	else if(handle->flags & NC_RDWR)
	{
		handle->xdrs->x_op = XDR_ENCODE ;
		if(handle->flags & NC_HDIRTY)
		{
			if(!xdr_cdf(handle->xdrs, &handle) )
				/* MEMORY: should xdr_free(xdr_cdf,
				 * &handle) be called? */
				return(-1) ;
		}
		else if(handle->flags & NC_NDIRTY)
		{
			if(!xdr_numrecs(handle->xdrs, handle) )
				/* MEMORY: should xdr_free(xdr_numrecs,
				 * handle) be called? */
				return(-1) ;
		}
	}

	NC_free_cdf(handle) ; /* calls close */

	_cdfs[cdfid] = NULL ;

	if(cdfid == _ncdf - 1)
		_ncdf-- ;

	return(0) ;
}

int
ncsetfill(id, fillmode)
int id ;
int fillmode ;
{
	NC *handle ;
	int ret = 0 ;

	cdf_routine_name = "ncsetfill" ;

	handle = NC_check_id(id) ; 
	if(handle == NULL)
		return(-1) ;

	if(!(handle->flags & NC_RDWR))
	{
		/* file isn't writable */
		NCadvise(NC_EPERM, "%s is not writable", handle->path) ;
		return -1 ;
	}

	ret = (handle->flags & NC_NOFILL) ? NC_NOFILL : NC_FILL ;

	if(fillmode == NC_NOFILL)
		handle->flags |= NC_NOFILL ;
	else if(fillmode == NC_FILL)
	{
		if(handle->flags & NC_NOFILL)
		{
			/*
			 * We are changing back to fill mode
			 * so do a sync
			 */
			if(handle->flags & NC_HDIRTY)
			{
				if(!xdr_cdf(handle->xdrs, &handle) )
					/* MEMORY: should xdr_free(xdr_cdf,
					 * &handle) be called? */
					return(-1) ;
				handle->flags &= ~(NC_NDIRTY | NC_HDIRTY) ;
			}
			else if(handle->flags & NC_NDIRTY)
			{
				if(!xdr_numrecs(handle->xdrs, handle) )
					/* MEMORY: should xdr_free(xdr_numrecs,
					 * handle) be called? */
					return(-1) ;
				handle->flags &= ~(NC_NDIRTY) ;
			}
		}
		handle->flags &= ~NC_NOFILL ;
	}
	else
	{
		NCadvise(NC_EINVAL, "Bad fillmode") ;
		return -1 ;
	}


	return ret ;
}
