/*
 *	file.c
 *
 *	file accessing module
 */

#include "fd.h"
#include "time.h"
#include "termio.h"
#include "realpath.h"
#include "kconv.h"
#include "func.h"
#include "kanji.h"

#ifdef	DEP_URLPATH
#include "urldisk.h"
#endif
#if	MSDOS
#include <process.h>
#endif

#define	MAXTMPNAMLEN		8
#define	LOCKEXT			"LCK"
#ifndef	DEP_DOSDRIVE
#define	DOSDIRENT		32
#define	LFNENTSIZ		13
#endif
#define	LFNONLY			" +,;=[]"
#define	DOSBODYLEN		8
#define	DOSEXTLEN		3
#define	FAT_NONE		0
#define	FAT_PRIMAL		1
#define	FAT_LFN			2
#define	FAT_DOSDRIVE		3

#if	MSDOS
extern int getcurdrv __P_((VOID_A));
#endif
#if	defined (DEP_DOSDRIVE) && defined (DEP_DOSLFN)
extern int checkdrive __P_((int));
#endif

extern int mark;
extern char fullpath[];
extern char *tmpfilename;
extern int physical_path;
#ifdef	DEP_PSEUDOPATH
extern char *unixpath;
#endif

#ifndef	NOFLOCK
static int NEAR fcntllock __P_((int, int));
#endif
static char *NEAR excllock __P_((CONST char *, int));
#ifdef	DEP_PSEUDOPATH
static int NEAR pushswap __P_((VOID_A));
static VOID NEAR popswap __P_((int));
static int NEAR realchdir __P_((CONST char *));
static char *NEAR realgetwd __P_((char *));
static int NEAR realmkdir __P_((CONST char *, int));
static int NEAR realrmdir __P_((CONST char *));
#else
#define	realchdir		Xchdir
#define	realgetwd		Xgetwd
#define	realmkdir		Xmkdir
#define	realrmdir		Xrmdir
#endif
static int NEAR retrievecwd __P_((char *, CONST char *));
static int NEAR cpfile __P_((CONST char *, CONST char *,
		struct stat *, struct stat *));
static VOID changemes __P_((VOID_A));
static int NEAR genrand __P_((int));
static int dounlink __P_((CONST char *));
static int dormdir __P_((CONST char *));
#ifndef	_NOWRITEFS
static int NEAR isexist __P_((CONST char *));
static int NEAR realdirsiz __P_((CONST char *, int, int, int, int));
static int NEAR getnamlen __P_((int, int, int, int, int));
static int NEAR saferename __P_((CONST char *, CONST char *));
static char *NEAR maketmpfile __P_((int, int, CONST char *, CONST char *));
#if	!MSDOS
static off_t NEAR getdirblocksize __P_((CONST char *));
static u_char *NEAR getentnum __P_((CONST char *, off_t));
#endif
static VOID NEAR restorefile __P_((char *, char *, char *));
#endif	/* !_NOWRITEFS */

char *deftmpdir = NULL;
#if	!defined (_USEDOSCOPY) && !defined (_NOEXTRACOPY)
int inheritcopy = 0;
#endif
short tmpumask = (short)0;

#ifdef	DEP_PSEUDOPATH
static int pseudodrv = -1;
static int *swaplist[] = {
# ifdef	DEP_DOSDRIVE
	&dosdrive,
# endif
# ifdef	DEP_URLPATH
	&urldrive,
# endif
};
#define	SWAPLISTSIZ		arraysize(swaplist)
#endif	/* DEP_PSEUDOPATH */


#ifdef	DEP_DOSEMU
CONST char *nodospath(path, file)
char *path;
CONST char *file;
{
	if (!_dospath(file)) return(file);
	path[0] = '.';
	path[1] = _SC_;
	Xstrcpy(&(path[2]), file);

	return(path);
}
#endif	/* DEP_DOSEMU */

int getstatus(namep)
namelist *namep;
{
#ifdef	DEP_DOSEMU
	char path[MAXPATHLEN];
#endif
	struct stat st, lst;
	CONST char *cp;

	cp = nodospath(path, namep -> name);
	if (Xlstat(cp, &lst) < 0 || stat2(cp, &st) < 0) return(-1);
	namep -> flags = 0;
	if (s_isdir(&st)) namep -> flags |= F_ISDIR;
	if (s_islnk(&lst)) namep -> flags |= F_ISLNK;

	if (isdisplnk(dispmode))
		memcpy((char *)&lst, (char *)&st, sizeof(struct stat));
#if	!MSDOS
	if (s_ischr(&lst) || s_isblk(&lst)) namep -> flags |= F_ISDEV;
#endif

	namep -> st_mode = lst.st_mode;
	namep -> st_nlink = lst.st_nlink;
#ifndef	NOUID
	namep -> st_uid = lst.st_uid;
	namep -> st_gid = lst.st_gid;
#endif
#if	MSDOS
	namep -> st_size = lst.st_size;
#else
	namep -> st_size = isdev(namep) ? (off_t)(lst.st_rdev) : lst.st_size;
#endif
#ifdef	HAVEFLAGS
	namep -> st_flags = lst.st_flags;
#endif
	namep -> st_mtim = lst.st_mtime;
	namep -> flags |= logical_access2(&st);
	namep -> tmpflags |= F_STAT;

	return(0);
}

int cmplist(vp1, vp2)
CONST VOID_P vp1;
CONST VOID_P vp2;
{
	namelist *namep1, *namep2;
	char *cp1, *cp2;
	int tmp;

	namep1 = (namelist *)vp1;
	namep2 = (namelist *)vp2;
	if (!sorton) return(namep1 -> ent - namep2 -> ent);

#ifndef	_NOPRECEDE
	if (!hasstat(namep1) && getstatus(namep1) < 0) return(1);
	if (!hasstat(namep2) && getstatus(namep2) < 0) return(-1);
#endif

	if (!isdir(namep1) && isdir(namep2)) return(1);
	if (isdir(namep1) && !isdir(namep2)) return(-1);
	tmp = isdotdir(namep2 -> name) - isdotdir(namep1 -> name);
	if (tmp) return(tmp);
	cp1 = namep1 -> name;
	cp2 = namep2 -> name;

	tmp = 0;
	switch (sorton & SRT_TYPE) {
		case SRT_LENGTH:
			tmp = (int)strlen(cp1) - (int)strlen(cp2);
			if (tmp) break;
/*FALLTHRU*/
		case SRT_FILENAME:
			tmp = strverscmp2(cp1, cp2);
			break;
		case SRT_EXTENSION:
			if (isdir(namep1)) {
				tmp = strverscmp2(cp1, cp2);
				break;
			}
			cp1 += strlen(cp1);
			cp2 += strlen(cp2);
			for (;;) {
				while (cp1 > namep1 -> name)
					if (*(--cp1) == '.') break;
				while (cp2 > namep2 -> name)
					if (*(--cp2) == '.') break;
				if (cp2 <= namep2 -> name) {
					tmp = (cp1 > namep1 -> name) ? 1 : 0;
					break;
				}
				if (cp1 <= namep1 -> name) {
					tmp = -1;
					break;
				}
				tmp = strverscmp2(cp1 + 1, cp2 + 1);
				if (tmp) break;
			}
			break;
		case SRT_SIZE:
			if (isdir(namep1)) tmp = strverscmp2(cp1, cp2);
			else if (namep1 -> st_size < namep2 -> st_size)
				tmp = -1;
			else if (namep1 -> st_size > namep2 -> st_size)
				tmp = 1;
			break;
		case SRT_DATE:
			if (namep1 -> st_mtim < namep2 -> st_mtim)
				tmp = -1;
			else if (namep1 -> st_mtim > namep2 -> st_mtim)
				tmp = 1;
			break;
		default:
			break;
	}

	if (sorton & SRT_DESC) tmp = -tmp;
	if (!tmp) tmp = namep1 -> ent - namep2 -> ent;

	return(tmp);
}

#ifndef	_NOTREE
int cmptree(vp1, vp2)
CONST VOID_P vp1;
CONST VOID_P vp2;
{
	treelist *tp1, *tp2;
	CONST char *cp1, *cp2;
	int tmp;

	tp1 = (treelist *)vp1;
	tp2 = (treelist *)vp2;
	if (!(tp1 -> name)) return((tp2 -> name) ? 1 : 0);
	if (!(tp2 -> name)) return(-1);
	cp1 = tp1 -> name;
	cp2 = tp2 -> name;

	tmp = 0;
	switch (sorton & SRT_TYPE) {
		case SRT_LENGTH:
			tmp = (int)strlen(cp1) - (int)strlen(cp2);
			if (tmp) break;
/*FALLTHRU*/
		case SRT_FILENAME:
		case SRT_EXTENSION:
			tmp = strverscmp2(cp1, cp2);
			break;
		default:
			break;
	}
	if (sorton & SRT_DESC) tmp = -tmp;

	return(tmp);
}
#endif	/* !_NOTREE */

/*ARGSUSED*/
struct dirent *searchdir(dirp, regexp, arcregstr)
DIR *dirp;
CONST reg_ex_t *regexp;
CONST char *arcregstr;
{
	struct dirent *dp;
#ifndef	_NOARCHIVE
	struct stat st;
	namelist tmp;
	int i;
#endif

	if (regexp) while ((dp = Xreaddir(dirp))) {
		if (regexp_exec(regexp, dp -> d_name, 1)) break;
	}
#ifndef	_NOARCHIVE
	else if (arcregstr) while ((dp = Xreaddir(dirp))) {
		if (stat2(dp -> d_name, &st) < 0 || s_isdir(&st)) continue;

		tmp.name = dp -> d_name;
		tmp.flags = 0;
		tmp.tmpflags = F_STAT;
		i = searcharcf(arcregstr, &tmp, 1, -1);
		if (i < 0) return(NULL);
		if (i) break;
	}
#endif
	else dp = Xreaddir(dirp);

	return(dp);
}

int underhome(buf)
char *buf;
{
	static char *homedir = NULL;
	CONST char *cp;
	char tmp[MAXPATHLEN], cwd[MAXPATHLEN];
	int len;

#if	MSDOS
	if (!buf) return(1);
#endif
	if (!Xgetwd(cwd)) {
		lostcwd(cwd);
		return(-1);
	}
	if (!homedir) {
		if (!(cp = gethomedir())) return(-1);
#ifdef	DEP_DOSDRIVE
		if (dospath2(cp)) return(-1);
#endif
#ifdef	DEP_URLPATH
		if (urlpath(cp, NULL, NULL, NULL)) return(-1);
#endif
		if (getrealpath(cp, tmp, cwd) != tmp) return(-1);
		homedir = Xstrdup(tmp);
	}

	if (buf && !physical_path) Xstrcpy(cwd, fullpath);
	len = strlen(homedir);
#if	MSDOS
	if (len <= 3) cp = NULL;
#else
	if (len <= 1) cp = NULL;
#endif
	else cp = underpath(cwd, homedir, len);

	if (buf) Xstrcpy(buf, (cp) ? cp : nullstr);
#ifdef	DEBUG
	Xfree(homedir);
	homedir = NULL;
#endif

	return((cp) ? 1 : 0);
}

int preparedir(dir)
CONST char *dir;
{
	struct stat st;
	char tmp[MAXPATHLEN];
	CONST char *cp;

	cp = dir;
#ifdef	DEP_DOSPATH
	if (_dospath(dir)) cp += 2;
#endif
	if (!isdotdir(cp)) {
		if (stat2(dir, &st) < 0) {
			if (errno != ENOENT) return(-1);
			if (Xmkdir(dir, 0777) < 0) {
#if	MSDOS
				if (errno != EEXIST) return(-1);
				if (cp[0] != _SC_ || cp[1]) {
					cp = getbasename(dir);
					if (cp[0] != '.' || cp[1]) return(-1);
				}
				st.st_mode = S_IFDIR;
#else
				return(-1);
#endif
			}
			else if (stat2(dir, &st) < 0) return(-1);
		}
		if (!s_isdir(&st)) return(seterrno(ENOTDIR));
	}
	VOID_C entryhist(Xrealpath(dir, tmp, 0), HST_PATH | HST_UNIQ);

	return(0);
}

#ifndef	NOFLOCK
static int NEAR fcntllock(fd, operation)
int fd, operation;
{
	int i, n;

# ifdef	DEP_PSEUDOPATH
	if (chkopenfd(fd) != DEV_NORMAL) return(1);
# endif

	n = -1;
	errno = 0;
	for (i = 0; i < LCK_MAXRETRY; i++) {
		n = Xflock(fd, operation);
		if (n >= 0) {
			n = 1;
			break;
		}
# ifdef	EACCES
		else if (errno == EACCES) /*EMPTY*/;
# endif
# ifdef	EAGAIN
		else if (errno == EAGAIN) /*EMPTY*/;
# endif
# ifdef	EWOULDBLOCK
		else if (errno == EWOULDBLOCK) /*EMPTY*/;
# endif
		else break;

		if (intrkey(-1)) {
			errno = EINTR;
			break;
		}
# if	!MSDOS || defined (DJGPP)
		usleep(100000L);
# endif
	}
	if (i >= LCK_MAXRETRY) return(0);

	return(n);
}
#endif	/* !NOFLOCK */

static char *NEAR excllock(file, operation)
CONST char *file;
int operation;
{
#if	MSDOS
	char *ext;
#endif
	static char **locklist = NULL;
	struct stat st;
	char *cp, path[MAXPATHLEN];
	int i, fd;

	if (!file) {
		if (locklist) {
			for (i = 0; locklist[i]; i++) {
				VOID_C unlink(locklist[i]);
				Xfree(locklist[i]);
			}
			Xfree(locklist);
		}
		return(NULL);
	}

	if (operation & LOCK_UN) {
		VOID_C Xunlink(file);
		if (locklist) {
			for (i = 0; locklist[i]; i++)
				if (file == locklist[i]) break;
			if (locklist[i]) {
				for (; locklist[i + 1]; i++)
					locklist[i] = locklist[i + 1];
				locklist[i] = NULL;
			}
			if (!i) {
				Xfree(locklist);
				locklist = NULL;
			}
		}
		return(NULL);
	}

#if	MSDOS
	cp = getbasename(file);
	for (i = 0; cp[i]; i++) if (cp[i] == '.') break;
	ext = &(cp[i]);
	if (i > DOSBODYLEN - strsize(LOCKEXT))
		i = DOSBODYLEN - strsize(LOCKEXT);
	i += cp - file;
	VOID_C Xsnprintf(path, sizeof(path),
		"%-.*s%s%s", i, file, LOCKEXT, ext);
#else
	VOID_C Xsnprintf(path, sizeof(path), "%s.%s", file, LOCKEXT);
#endif

	fd = -1;
	for (i = 0; i < LCK_MAXRETRY; i++) {
		if (!(operation & LOCK_SH))
			fd = Xopen(path,
				O_BINARY | O_WRONLY | O_CREAT | O_EXCL,
				0666 & ~tmpumask);
		else if (Xlstat(path, &st) >= 0) fd = seterrno(EEXIST);
		else fd = (errno == ENOENT) ? 0 : -1;
		if (fd >= 0 || errno != EEXIST) break;

		if (intrkey(-1)) {
			errno = EINTR;
			break;
		}
#if	!MSDOS || defined (DJGPP)
		usleep(100000L);
#endif
	}
	if (i >= LCK_MAXRETRY) return(vnullstr);
	if (fd < 0) return(NULL);
	if (operation & LOCK_SH) return((char *)file);

	VOID_C Xclose(fd);
	cp = Xstrdup(path);
	i = countvar(locklist);
	locklist = (char **)Xrealloc(locklist, (i + 2) * sizeof(char *));
	locklist[i] = cp;
	locklist[++i] = NULL;

	return(cp);
}

lockbuf_t *lockopen(path, flags, mode)
CONST char *path;
int flags, mode;
{
#ifndef	NOFLOCK
	int n;
#endif
	char *lckname;
	lockbuf_t *lck;
	int fd, err, lckflags, operation, duperrno;

#ifdef	FAKEUNINIT
	fd = -1;	/* fake for -Wuninitialized */
#endif
	lckname = NULL;
	lckflags = 0;
	operation = ((flags & O_ACCMODE) == O_RDONLY) ? LOCK_SH : LOCK_EX;
	operation |= LOCK_NB;
	err = 0;

#ifndef	NOFLOCK
	if (isnfs(path) <= 0) {
		lckflags |= LCK_FLOCK;
# ifdef	NOFTRUNCATE
		fd = newdup(Xopen(path, flags, mode));
# else
		fd = newdup(Xopen(path, flags & ~O_TRUNC, mode));
# endif
		if (fd < 0) {
			if ((flags & O_ACCMODE) == O_WRONLY || errno != ENOENT)
				return(NULL);
		}
		else if ((n = fcntllock(fd, operation)) <= 0) {
			VOID_C Xclose(fd);
			if (n < 0) return(NULL);
			err++;
			lckflags &= ~LCK_FLOCK;
		}
# ifndef	NOFTRUNCATE
		else if ((flags & O_TRUNC) && Xftruncate(fd, (off_t)0) < 0) {
			duperrno = errno;
			VOID_C fcntllock(fd, LOCK_UN);
			VOID_C Xclose(fd);
			errno = duperrno;
			return(NULL);
		}
# endif
	}
#endif	/* !NOFLOCK */

	if (!(lckflags & LCK_FLOCK)) {
		if (!(lckname = excllock(path, operation))) return(NULL);
		else if (operation & LOCK_SH) {
			if (lckname == vnullstr) err++;
			lckname = NULL;
		}
		else if (lckname == vnullstr) return(NULL);

		if ((fd = newdup(Xopen(path, flags, mode))) >= 0) /*EMPTY*/;
		else if ((flags & O_ACCMODE) == O_WRONLY || errno != ENOENT) {
			duperrno = errno;
			if (lckname) {
				VOID_C excllock(lckname, LOCK_UN);
				Xfree(lckname);
			}
			errno = duperrno;
			return(NULL);
		}
	}

	if (fd < 0) lckflags |= LCK_INVALID;
	else if (err) {
		VOID_C Xfprintf(Xstderr, "%k: %k\r\n", path, NOLCK_K);
		if (isttyiomode) warning(0, HITKY_K);
	}

	lck = (lockbuf_t *)Xmalloc(sizeof(lockbuf_t));
	lck -> fd = fd;
	lck -> fp = NULL;
	lck -> name = lckname;
	lck -> flags = lckflags;

	return(lck);
}

lockbuf_t *lockfopen(path, type, flags, umask)
CONST char *path, *type;
int flags, umask;
{
	lockbuf_t *lck;
	XFILE *fp;

	if (!(lck = lockopen(path, flags, 0666 & ~umask))) return(NULL);

	if (!(lck -> flags & LCK_INVALID)) {
		if (!(fp = Xfdopen(lck -> fd, type))) {
			lockclose(lck);
			return(NULL);
		}
		lck -> fp = fp;
	}
	lck -> flags |= LCK_STREAM;

	return(lck);
}

VOID lockclose(lck)
lockbuf_t *lck;
{
	int duperrno;

	if (!lck) return;

	duperrno = errno;
	if (lck -> name) {
		VOID_C excllock(lck -> name, LOCK_UN);
		Xfree(lck -> name);
	}
	if (!(lck -> flags & LCK_INVALID)) {
#ifndef	NOFLOCK
		if (lck -> flags & LCK_FLOCK)
			VOID_C fcntllock(lck -> fd, LOCK_UN);
#endif
		if (lck -> flags & LCK_STREAM)
			VOID_C Xfclose(lck -> fp);
		else VOID_C Xclose(lck -> fd);
	}
	errno = duperrno;

	Xfree(lck);
}

int touchfile(path, stp)
CONST char *path;
struct stat *stp;
{
#ifndef	_NOEXTRAATTR
	int i;
#endif
#ifndef	NOUID
	u_id_t uid;
	g_id_t gid;
#endif
	struct utimes_t ut;
	struct stat st;
	u_int mode;
	int ret, duperrno;

	if (!(stp -> st_nlink)) return(0);
	if (Xlstat(path, &st) < 0) return(-1);
	if (s_islnk(&st)) return(1);

	ret = 0;
#if	MSDOS
	if (!(st.st_mode & S_IWUSR)) Xchmod(path, (st.st_mode | S_IWUSR));
#endif

	if (stp -> st_nlink & (TCH_ATIME | TCH_MTIME)) {
		if (!(stp -> st_nlink & TCH_ATIME))
			stp -> st_atime = st.st_atime;
		if (!(stp -> st_nlink & TCH_MTIME))
			stp -> st_mtime = st.st_mtime;
		ut.actime = stp -> st_atime;
		ut.modtime = stp -> st_mtime;
		duperrno = errno;
		if (Xutimes(path, &ut) >= 0) errno = duperrno;
		else ret = -1;
	}

#ifdef	HAVEFLAGS
	if (stp -> st_nlink & TCH_FLAGS) {
		duperrno = errno;
		if (Xchflags(path, stp -> st_flags) >= 0) errno = duperrno;
		else ret = -1;
	}
#endif

	if (stp -> st_nlink & TCH_MODE) {
		mode = stp -> st_mode;
		mode &= ~S_IFMT;
		mode |= (st.st_mode & S_IFMT);
#ifndef	_NOEXTRAATTR
		if ((stp -> st_nlink & TCH_MODEEXE)
		&& !s_isdir(&st) && !(st.st_mode & S_IEXEC_ALL)) {
			for (i = 0; i < 3; i++) {
				if (!(stp -> st_mode & (1 << (i + 12))))
					continue;
				mode &= ~(1 << (i * 3));
				mode |= (st.st_mode & (1 << (i * 3)));
			}
		}
		if (stp -> st_nlink & TCH_MASK) {
			mode &= ~(stp -> st_size);
			mode |= (st.st_mode & (stp -> st_size));
		}
#endif	/* !_NOEXTRAATTR */
		duperrno = errno;
		if (Xchmod(path, mode) >= 0) errno = duperrno;
		else ret = -1;
	}
#if	MSDOS
	else if (!(st.st_mode & S_IWUSR)) {
		duperrno = errno;
		Xchmod(path, st.st_mode);
		errno = duperrno;
	}
#endif

#ifndef	NOUID
	if (stp -> st_nlink & (TCH_UID | TCH_GID)) {
		if (!(stp -> st_nlink & TCH_UID)) stp -> st_uid = (uid_t)-1;
		if (!(stp -> st_nlink & TCH_GID)) stp -> st_gid = (gid_t)-1;
		uid = convuid(stp -> st_uid);
		gid = convgid(stp -> st_gid);
		duperrno = errno;
		if (Xchown(path, uid, gid) >= 0) errno = duperrno;
		else if (!(stp -> st_nlink & TCH_CHANGE)) {
			Xchown(path, (u_id_t)-1, gid);
			errno = duperrno;
		}
		else ret = -1;
	}
#endif	/* !NOUID */
	if (stp -> st_nlink & TCH_IGNOREERR) ret = 0;

	return(ret);
}

#ifdef	DEP_PSEUDOPATH
static int NEAR pushswap(VOID_A)
{
	int i, flags;

	flags = 0;
	for (i = 0; i < SWAPLISTSIZ; i++) {
		flags <<= 2;
		flags |= (*(swaplist[i]) & 3);
		*(swaplist[i]) = 0;
	}

	return(flags);
}

static VOID NEAR popswap(flags)
int flags;
{
	int i;

	for (i = SWAPLISTSIZ - 1; i >= 0; i--) {
		*(swaplist[i]) = (flags & 3);
		flags >>= 2;
	}
}

static int NEAR realchdir(path)
CONST char *path;
{
	int n, flags;

	flags = pushswap();
	n = Xchdir(path);
	popswap(flags);

	return(n);
}

static char *NEAR realgetwd(path)
char *path;
{
	char *cp;
	int flags;

	flags = pushswap();
	cp = Xgetwd(path);
	popswap(flags);

	return(cp);
}

int reallstat(path, stp)
CONST char *path;
struct stat *stp;
{
	int n, flags;

	flags = pushswap();
	n = Xlstat(path, stp);
	popswap(flags);

	return(n);
}

static int NEAR realmkdir(path, mode)
CONST char *path;
int mode;
{
	int n, flags;

	flags = pushswap();
	n = Xmkdir(path, mode);
	popswap(flags);

	return(n);
}

static int NEAR realrmdir(path)
CONST char *path;
{
	int n, flags;

	flags = pushswap();
	n = Xrmdir(path);
	popswap(flags);

	return(n);
}
#endif	/* DEP_PSEUDOPATH */

static int NEAR retrievecwd(cwd, path)
char *cwd;
CONST char *path;
{
	if (!path || realchdir(path) < 0 || !realgetwd(cwd)) return(-1);

	return(0);
}

VOID lostcwd(path)
char *path;
{
	CONST char *cp;
	char buf[MAXPATHLEN];
	int duperrno;

	duperrno = errno;
	if (!path) path = buf;
#ifdef	FAKEUNINIT
	cp = NULL;	/* fake for -Wuninitialized */
#endif

	if (path != fullpath && retrievecwd(path, fullpath) >= 0) cp = NOCWD_K;
#ifdef	DEP_PSEUDOPATH
	else if (retrievecwd(path, unixpath) >= 0) cp = NOPSU_K;
#endif
	else if (retrievecwd(path, gethomedir()) >= 0) cp = GOHOM_K;
	else if (retrievecwd(path, rootpath) >= 0) cp = GOROT_K;
	else error(rootpath);

	if (path != fullpath) Xstrncpy(fullpath, path, MAXPATHLEN - 1);
	warning(0, cp);
#ifndef	_NOUSEHASH
	VOID_C searchhash(NULL, nullstr, nullstr);
#endif
	errno = duperrno;
}

#ifndef	NODIRLOOP
int issamebody(src, dest)
CONST char *src, *dest;
{
	struct stat st1, st2;

	if (Xstat(src, &st1) < 0 || Xstat(dest, &st2) < 0) return(0);
	if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) return(0);
# ifdef	DEP_URLPATH
	if (st1.st_dev == (dev_t)-1 || st1.st_ino == (ino_t)-1) return(0);
# endif

	return(1);
}
#endif	/* !NODIRLOOP */

#ifndef	NOSYMLINK
int cpsymlink(src, dest)
CONST char *src, *dest;
{
	struct stat st;
	char path[MAXPATHLEN];
	int len;

	if ((len = Xreadlink(src, path, strsize(path))) < 0) return(-1);
	if (Xlstat(dest, &st) >= 0) {
# ifndef	NODIRLOOP
		if (issamebody(src, dest)) return(0);
# endif
		if (Xunlink(dest) < 0) return(-1);
	}
	path[len] = '\0';
	if (Xsymlink(path, dest) < 0) return(-1);

	return(1);
}
#endif	/* !NOSYMLINK */

/*ARGSUSED*/
static int NEAR cpfile(src, dest, stp1, stp2)
CONST char *src, *dest;
struct stat *stp1, *stp2;
{
#if	MSDOS
	struct stat st;
#endif
#ifndef	NODIRLOOP
	int issame;
#endif
#ifndef	_NOEXTRACOPY
	off_t size;
#endif
	char buf[BUFSIZ];
	off_t total;
	int n, fd1, fd2, tty, flags, mode, timeout, duperrno;

#if	MSDOS
	if (!stp2 && Xlstat(dest, &st) >= 0) stp2 = &st;
	if (stp2 && !(stp2 -> st_mode & S_IWUSR))
		Xchmod(src, stp2 -> st_mode | S_IWUSR);
#endif
#ifndef	NOSYMLINK
	if (s_islnk(stp1)) {
# ifdef	DEP_URLPATH
		fd1 = urlpath(src, NULL, NULL, NULL);
		fd2 = urlpath(dest, NULL, NULL, NULL);
		if (fd1 != fd2) {
			if (Xstat(src, stp1) < 0) return(-1);
		}
		else
# endif
		if (cpsymlink(src, dest) < 0) return(-1);
		else return(0);
	}
#endif	/* !NOSYMLINK */

	flags = (O_BINARY | O_RDONLY);
	mode = stp1 -> st_mode;
#ifndef	NODIRLOOP
	issame = issamebody(src, dest);
#endif
	if ((fd1 = Xopen(src, flags, mode)) < 0) return(-1);

	flags = (O_BINARY | O_WRONLY | O_CREAT | O_TRUNC);
#if	MSDOS
	mode |= S_IWUSR;
#endif
#ifndef	NODIRLOOP
	if (issame) {
		flags |= O_EXCL;
		if (Xunlink(dest) < 0) {
			VOID_C Xclose(fd1);
			return(-1);
		}
	}
#endif
	if ((fd2 = Xopen(dest, flags, mode)) < 0) {
		VOID_C Xclose(fd1);
		return(-1);
	}

	tty = isatty(fd1);
	timeout = 0;
#ifdef	DEP_URLPATH
	switch (chkopenfd(fd1)) {
		case DEV_URL:
		case DEV_HTTP:
			timeout = urltimeout;
			VOID_C urlfstat(fd1, stp1);
			break;
		default:
			break;
	}
#endif

	total = (off_t)0;
	if (tty) stp1 -> st_size = (off_t)-1;
	for (;;) {
		if (stp1 -> st_size >= (off_t)0 && total >= stp1 -> st_size)
			n = 0;
		else n = checkread(fd1, buf, sizeof(buf), timeout);
		if (n < 0) break;
		total += (off_t)n;
		if (!n) {
			if (total < stp1 -> st_size) n = seterrno(ETIMEDOUT);
			break;
		}
#ifndef	_NOEXTRACOPY
		size = (off_t)n;
		showprogress(&size);
#endif
		if ((n = surewrite(fd2, buf, n)) < 0) break;
	}

	safeclose(fd2);
	safeclose(fd1);
#ifndef	_NOEXTRACOPY
	fshowprogress(dest);
#endif

	if (n < 0) {
		duperrno = errno;
		VOID_C Xunlink(dest);
		errno = duperrno;
		return(-1);
	}
	stp1 -> st_nlink = (TCH_ATIME | TCH_MTIME | TCH_IGNOREERR);
#if	defined (_USEDOSCOPY) || !defined (_NOEXTRACOPY)
# ifndef	_USEDOSCOPY
	if (!inheritcopy) /*EMPTY*/;
	else
# endif
	if (touchfile(dest, stp1) < 0) return(-1);
#endif	/* _USEDOSCOPY || !_NOEXTRACOPY */

	return(0);
}

static VOID changemes(VOID_A)
{
	warning(0, CHGFD_K);
}

int safecpfile(src, dest, stp1, stp2)
CONST char *src, *dest;
struct stat *stp1, *stp2;
{
#ifdef	DEP_DOSDRIVE
	int drive;
#endif

	for (;;) {
		if (cpfile(src, dest, stp1, stp2) >= 0) break;
		if (errno && errno != ENOSPC) return(-1);
		for (;;) {
			if (!yesno(NOSPC_K)) return(seterrno(ENOSPC));
#ifdef	DEP_DOSDRIVE
			if ((drive = dospath3(dest))) {
				if (flushdrv(drive, changemes) < 0) continue;
			}
			else
#endif
			changemes();
			break;
		}
	}

	return(0);
}

int safemvfile(src, dest, stp1, stp2)
CONST char *src, *dest;
struct stat *stp1, *stp2;
{
	if (Xrename(src, dest) >= 0) {
#ifndef	_NOEXTRACOPY
		fshowprogress(dest);
#endif
		return(0);
	}
	if (errno != EXDEV || s_isdir(stp1)) return(-1);
	if (safecpfile(src, dest, stp1, stp2) < 0 || Xunlink(src) < 0)
		return(-1);

	stp1 -> st_nlink = (TCH_MODE | TCH_UID | TCH_GID
		| TCH_ATIME | TCH_MTIME | TCH_IGNOREERR);
	return (touchfile(dest, stp1));
}

static int NEAR genrand(max)
int max;
{
	static long last = -1L;
	time_t now;

	if (last < 0L) {
		now = Xtime(NULL);
		last = ((now & 0xff) << 16) + (now & ~0xff) + getpid();
	}

	do {
		last = last * (u_long)1103515245 + 12345;
	} while (last < 0L);

	return((last / 65537L) % max);
}

char *genrandname(buf, len)
char *buf;
int len;
{
	static char seq[] = {
#ifdef	PATHNOCASE
		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
		'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
		'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
		'U', 'V', 'W', 'X', 'Y', 'Z', '_'
#else
		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
		'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
		'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
		'u', 'v', 'w', 'x', 'y', 'z', '_'
#endif
	};
	int i, j, c;

	if (!buf) {
		for (i = 0; i < arraysize(seq); i++) {
			j = genrand(arraysize(seq));
			c = seq[i];
			seq[i] = seq[j];
			seq[j] = c;
		}
	}
	else {
		for (i = 0; i < len; i++) {
			j = genrand(arraysize(seq));
			buf[i] = seq[j];
		}
		buf[i] = '\0';
	}

	return(buf);
}

int mktmpdir(dir)
char *dir;
{
	char *cp, path[MAXPATHLEN];
	int n, len, mask;

	if (!deftmpdir || !*deftmpdir || !dir) return(seterrno(ENOENT));
	VOID_C Xrealpath(deftmpdir, path, RLP_READLINK);
	Xfree(deftmpdir);
#if	defined (DEP_DOSDRIVE) && defined (DEP_DOSLFN)
	if (checkdrive(Xtoupper(path[0]) - 'A') && !realgetwd(path)) {
		lostcwd(path);
		deftmpdir = NULL;
		return(-1);
	}
#endif
	deftmpdir = Xstrdup(path);
#if	MSDOS
	*path = (Xisupper(getcurdrv())) ? Xtoupper(*path) : Xtolower(*path);
#endif

	mask = 0777 & ~tmpumask;
	cp = strcatdelim(path);
	if (tmpfilename) {
		Xstrcpy(cp, tmpfilename);
		if (Xaccess(path, R_OK | W_OK | X_OK) < 0) {
			Xfree(tmpfilename);
			tmpfilename = NULL;
		}
	}
	if (!tmpfilename) {
		n = strsize(TMPPREFIX);
		Xstrncpy(cp, TMPPREFIX, n);
		len = strsize(path) - (cp - path);
		if (len > MAXTMPNAMLEN) len = MAXTMPNAMLEN;
		len -= n;
		genrandname(NULL, 0);

		for (;;) {
			genrandname(&(cp[n]), len);
			if (realmkdir(path, mask) >= 0) break;
			if (errno != EEXIST) return(-1);
		}
		tmpfilename = Xstrdup(cp);
	}

	if (!(n = strlen(dir))) {
		Xstrcpy(dir, path);
		return(0);
	}

	Xstrncpy((cp = strcatdelim(path)), dir, n);
	len = strsize(path) - (cp - path);
	if (len > MAXTMPNAMLEN) len = MAXTMPNAMLEN;
	len -= n;
	genrandname(NULL, 0);

	for (;;) {
		genrandname(&(cp[n]), len);
		if (realmkdir(path, mask) >= 0) {
			Xstrcpy(dir, path);
			return(0);
		}
		if (errno != EEXIST) break;
	}

	if (cp > path) {
		*(--cp) = '\0';
		n = errno;
		realrmdir(path);
		errno = n;
	}

	return(-1);
}

int rmtmpdir(dir)
CONST char *dir;
{
	char path[MAXPATHLEN];

	if (dir && *dir && realrmdir(dir) < 0) return(-1);
	if (!deftmpdir || !*deftmpdir || !tmpfilename || !*tmpfilename)
		return(seterrno(ENOENT));
	strcatdelim2(path, deftmpdir, tmpfilename);
	if (realrmdir(path) >= 0) {
		Xfree(tmpfilename);
		tmpfilename = NULL;
	}
	else if (errno != ENOTEMPTY && errno != EEXIST && errno != EACCES)
		return(-1);

	return(0);
}

int opentmpfile(path, mode)
CONST char *path;
int mode;
{
	char *cp;
	int fd, len;

	cp = getbasename(path);
	len = MAXPATHLEN - (cp - path) - 1;
	if (len > MAXTMPNAMLEN) len = MAXTMPNAMLEN;
	genrandname(NULL, 0);

	for (;;) {
		genrandname(cp, len);
		fd = Xopen(path, O_BINARY | O_WRONLY | O_CREAT | O_EXCL, mode);
		if (fd >= 0) break;
		if (errno != EEXIST) return(-1);
	}

	return(fd);
}

int mktmpfile(file)
char *file;
{
	char path[MAXPATHLEN];
	int fd, duperrno;

	path[0] = '\0';
	if (mktmpdir(path) < 0) return(-1);
	VOID_C strcatdelim(path);
	if ((fd = opentmpfile(path, 0666 & ~tmpumask)) >= 0) {
		Xstrcpy(file, path);
		return(fd);
	}
	duperrno = errno;
	rmtmpdir(NULL);
	errno = duperrno;

	return(-1);
}

int rmtmpfile(file)
CONST char *file;
{
	if ((Xunlink(file) < 0 && errno != ENOENT) || rmtmpdir(NULL) < 0)
		return(-1);

	return(0);
}

static int dounlink(path)
CONST char *path;
{
	if (Xunlink(path) < 0) return(APL_ERROR);

	return(APL_OK);
}

static int dormdir(path)
CONST char *path;
{
	if (isdotdir(path)) return(APL_OK);
	if (Xrmdir(path) < 0) return(APL_ERROR);

	return(APL_OK);
}

VOID removetmp(dir, file)
char *dir;
CONST char *file;
{
#ifdef	DEP_DOSEMU
	char path[MAXPATHLEN];
#endif

	if (!dir || !*dir) /*EMPTY*/;
	else if (_chdir2(dir) < 0) {
		warning(-1, dir);
		*dir = '\0';
	}
	else if (!file) VOID_C applydir(NULL, dounlink,
		NULL, dormdir, ORD_NOPREDIR, NULL);
	else if (Xunlink(nodospath(path, file)) < 0) warning(-1, file);

	if (_chdir2(fullpath) < 0) lostcwd(fullpath);
#ifdef	DEP_PSEUDOPATH
	shutdrv(pseudodrv);
	pseudodrv = -1;
#endif
	if (dir) {
		if (*dir && rmtmpdir(dir) < 0) warning(-1, dir);
		Xfree(dir);
	}
}

int forcecleandir(dir, file)
CONST char *dir, *file;
{
#if	!MSDOS
	p_id_t pid;
#endif
	extern char **environ;
	char *argv[4], buf[MAXPATHLEN];

	VOID_C excllock(NULL, LOCK_UN);

	if (!dir || !*dir || !file || !*file) return(0);
	strcatdelim2(buf, dir, file);

	if (rawchdir(buf) < 0) return(0);
	VOID_C rawchdir(rootpath);

#if	MSDOS
	argv[0] = "DELTREE";
	argv[1] = "/Y";
#else
	argv[0] = "rm";
	argv[1] = "-rf";
#endif
	argv[2] = buf;
	argv[3] = NULL;

#if	MSDOS
	spawnve(P_WAIT, "DELTREE.EXE", argv, environ);
#else
	if ((pid = fork()) < (p_id_t)0) return(-1);
	else if (!pid) {
		execve("/bin/rm", argv, environ);
		_exit(1);
	}
#endif

	return(0);
}

#ifdef	DEP_PSEUDOPATH
char *dostmpdir(drive)
int drive;
{
	char path[MAXPATHLEN];

	path[0] = DOSTMPPREFIX;
	path[1] = drive;
	path[2] = '\0';
	if (mktmpdir(path) < 0) return(NULL);

	return(Xstrdup(path));
}

int tmpdosdupl(dir, dirp, single)
CONST char *dir;
char **dirp;
int single;
{
# ifdef	DEP_DOSEMU
	char tmp[MAXPATHLEN];
# endif
	struct stat st;
	char *cp, *tmpdir, path[MAXPATHLEN];
	int i, drive;

# ifdef	DEP_DOSDRIVE
	if ((drive = dospath2(dir))) /*EMPTY*/;
	else
# endif
# ifdef	DEP_URLPATH
	if (urlpath(dir, NULL, NULL, &drive)) drive += '0';
	else
# endif
	return(0);
	if (!(tmpdir = dostmpdir(drive))) {
		warning(-1, dir);
		return(-1);
	}

	Xstrcpy(path, tmpdir);
	cp = strcatdelim(path);
	waitmes();

	if (single || mark <= 0) {
		Xstrcpy(cp, filelist[filepos].name);
		st.st_mode = filelist[filepos].st_mode;
		st.st_atime = st.st_mtime = filelist[filepos].st_mtim;
		st.st_size = filelist[filepos].st_size;
		if (cpfile(fnodospath(tmp, filepos), path, &st, NULL) < 0) {
			warning(-1, filelist[filepos].name);
			removetmp(tmpdir, NULL);
			return(-1);
		}
	}
	else for (i = 0; i < maxfile; i++) {
		if (!ismark(&(filelist[i]))) continue;
		Xstrcpy(cp, filelist[i].name);
		st.st_mode = filelist[i].st_mode;
		st.st_atime = st.st_mtime = filelist[i].st_mtim;
		st.st_size = filelist[i].st_size;
		if (cpfile(fnodospath(tmp, i), path, &st, NULL) < 0) {
			warning(-1, filelist[i].name);
			removetmp(tmpdir, NULL);
			return(-1);
		}
	}

	if ((pseudodrv = preparedrv(fullpath, NULL, NULL)) < 0) {
		warning(-1, fullpath);
		removetmp(tmpdir, NULL);
		return(-1);
	}
	if (_chdir2(tmpdir) < 0) {
		warning(-1, tmpdir);
		removetmp(tmpdir, NULL);
		return(-1);
	}
	*dirp = tmpdir;

	return(drive);
}

int tmpdosrestore(drive, file)
int drive;
CONST char *file;
{
	struct stat st;
	char path[MAXPATHLEN];
# ifdef	DEP_DOSEMU
	char tmp[MAXPATHLEN];
# endif

# ifdef	DEP_DOSDRIVE
	if (isalpha(drive)) Xstrcpy(gendospath(path, drive, '\0'), file);
	else
# endif
	strcatdelim2(path, fullpath, file);
# ifdef	DEP_DOSEMU
	file = nodospath(tmp, file);
# endif
	waitmes();
	if (Xlstat(file, &st) < 0 || cpfile(file, path, &st, NULL) < 0)
		return(-1);

	return(0);
}
#endif	/* DEP_PSEUDOPATH */

#ifndef	_NOWRITEFS
static int NEAR isexist(file)
CONST char *file;
{
	struct stat st;

	if (Xlstat(file, &st) < 0 && errno == ENOENT) return(0);

	return(1);
}

static int NEAR realdirsiz(s, fat, boundary, dirsize, ofs)
CONST char *s;
int fat, boundary, dirsize, ofs;
{
	int i, len, lfn, dot;

	if (fat == FAT_NONE) {
		len = (strlen(s) + ofs + boundary) & ~(boundary - 1);
		return(len + dirsize);
	}

	if (fat > FAT_PRIMAL && !isdotdir(s)) {

		lfn = dot = 0;
		for (i = len = 0; s[i]; i++, len++) {
			if (s[i] == '.') {
				if (dot || !i || i > DOSBODYLEN) lfn = 1;
				dot = i + 1;
			}
			else if (iswsjis(s, i)) {
				i++;
				lfn = 1;
			}
			else if (lfn) /*EMPTY*/;
			else if (Xislower(s[i])) lfn = 1;
			else if (Xstrchr(LFNONLY, s[i])) lfn = 1;
		}
		if (lfn) /*EMPTY*/;
		else if (dot) {
			if (i - dot > DOSEXTLEN) lfn = 1;
		}
		else if (i > DOSBODYLEN) lfn = 1;

		len += ofs;
		if (lfn) return((len / LFNENTSIZ + 1) * DOSDIRENT + DOSDIRENT);
	}

	return(DOSDIRENT);
}

static int NEAR getnamlen(size, fat, boundary, dirsize, ofs)
int size, fat, boundary, dirsize, ofs;
{
	if (fat == FAT_NONE) {
		size -= dirsize;
		size = (size & ~(boundary - 1)) - 1 - ofs;
		if (size > MAXNAMLEN) size = MAXNAMLEN;
	}
	else if (size <= DOSDIRENT) size = DOSBODYLEN;
	else {
		size -= DOSDIRENT;
		size = (size / DOSDIRENT) * LFNENTSIZ - 1;
		if (size > DOSMAXNAMLEN) size = DOSMAXNAMLEN;
	}

	return(size);
}

static int NEAR saferename(from, to)
CONST char *from, *to;
{
#ifdef	DEP_DOSEMU
	char fpath[MAXPATHLEN], tpath[MAXPATHLEN];
#endif

	if (!strpathcmp2(from, to)) return(0);
#ifdef	DEP_DOSEMU
	from = nodospath(fpath, from);
	to = nodospath(tpath, to);
#endif

	return(Xrename(from, to));
}

/*ARGSUSED*/
static char *NEAR maketmpfile(len, fat, tmpdir, old)
int len, fat;
CONST char *tmpdir, *old;
{
	char *fname, path[MAXPATHLEN];
	int l, fd;

	if (len < 0) return(NULL);
	fname = Xmalloc(len + 1);
	genrandname(NULL, 0);

	if (tmpdir) l = strcatdelim2(path, tmpdir, NULL) - path;
#ifdef	FAKEUNINIT
	else l = 0;	/* fake for -Wuninitialized */
#endif

	for (;;) {
		genrandname(fname, len);
#ifndef	PATHNOCASE
		if (fat != FAT_NONE) Xstrtoupper(fname);
#endif
		if (tmpdir) {
			Xstrcpy(&(path[l]), fname);
			if (!isexist(path)) {
				if (old) {
#if	!MSDOS
					if (isexist(fname)) /*EMPTY*/;
					else
#endif
					if (saferename(old, fname) >= 0)
						return(fname);
#if	MSDOS
					else if (errno == EACCES) /*EMPTY*/;
#endif
					else break;
				}
				else {
					fd = Xopen(fname,
						O_WRONLY | O_CREAT | O_EXCL,
						0600);
					if (fd >= 0) {
						VOID_C Xclose(fd);
						return(fname);
					}
					if (errno != EEXIST) break;
				}
			}
		}
		else {
			if (Xmkdir(fname, 0777) >= 0) return(fname);
			if (errno != EEXIST) break;
		}
	}
	Xfree(fname);

	return(NULL);
}

#if	!MSDOS
static off_t NEAR getdirblocksize(dir)
CONST char *dir;
{
	struct stat st;

	if (Xlstat(dir, &st) < 0) return(getblocksize(dir));

	return((off_t)st.st_size);
}

static u_char *NEAR getentnum(dir, bsiz)
CONST char *dir;
off_t bsiz;
{
	struct stat st;
	u_char ch, *tmp;
	int i, n, fd;

	if (Xlstat(dir, &st) < 0
	|| (fd = Xopen(dir, O_BINARY | O_RDONLY, 0666)) < 0)
		return(NULL);
	n = (off_t)(st.st_size) / bsiz;
	tmp = (u_char *)Xmalloc(n + 1);

	for (i = 0; i < n; i++) {
		if (Xlseek(fd, (off_t)(i * bsiz + 3), L_SET) < 0) {
			Xfree(tmp);
			return(NULL);
		}
		if (sureread(fd, &ch, sizeof(ch)) < 0) {
			Xfree(tmp);
			return(NULL);
		}
		tmp[i] = ch + 1;
	}
	tmp[i] = 0;
	VOID_C Xclose(fd);

	return(tmp);
}
#endif	/* !MSDOS */

static VOID NEAR restorefile(dir, path, fnamp)
char *dir, *path, *fnamp;
{
	DIR *dirp;
	struct dirent *dp;
	int n;

	if (!(dirp = Xopendir(dir))) warning(-1, dir);
	else {
		while ((dp = Xreaddir(dirp))) {
			if (isdotdir(dp -> d_name)) continue;

			n = strcatpath(path, fnamp, dp -> d_name);
			if (n < 0 || saferename(path, dp -> d_name) < 0)
				warning(-1, path);
		}
		VOID_C Xclosedir(dirp);
	}
	if (Xrmdir(dir) < 0) warning(-1, dir);
	Xfree(dir);
}

VOID arrangedir(fs)
int fs;
{
#if	!MSDOS
	off_t persec, totalent, dirblocksize;
	u_char *entnum;
	char **tmpfiles;
	int tmpno, block, ptr, totalptr, headbyte;
#endif
#ifndef	PATHNOCASE
	int duppathignorecase;
#endif
	DIR *dirp;
	struct dirent *dp;
	int fat, boundary, dirsize, namofs;
	int i, n, top, size, len, ent;
	CONST char *cp;
	char *tmp, *tmpdir, *fnamp, **fnamelist, path[MAXPATHLEN];

	switch (fs) {
#if	!MSDOS
		case FSID_EFS:		/* IRIX File System */
			fat = FAT_NONE;
			headbyte = 4;
			boundary = 2;
			dirsize = sizeof(u_long);
			namofs = 0;
			break;
		case FSID_SYSV:		/* SystemV R3 File System */
			fat = FAT_NONE;
			headbyte = 0;
			boundary = 8;
			dirsize = sizeof(u_short);
			namofs = 0;
			break;
		case FSID_LINUX:	/* Linux File System */
			fat = FAT_NONE;
			headbyte = 0;
			boundary = 4;
			dirsize = 4;	/* short + short */
			namofs = 3;
			break;
# ifdef	DEP_DOSDRIVE
		case FSID_DOSDRIVE:	/* Windows95 File System on DOSDRIVE */
			fat = FAT_DOSDRIVE;
			headbyte = -1;
			boundary = LFNENTSIZ;
			dirsize = DOSDIRENT;
			namofs = 0;
			break;
# endif
#endif	/* !MSDOS */
		case FSID_FAT:		/* MS-DOS File System */
			fat = FAT_PRIMAL;
#if	!MSDOS
			headbyte = -1;
#endif
			boundary = 1;
			dirsize = DOSDIRENT;
			namofs = 0;
			break;
		case FSID_LFN:		/* Windows95 File System */
			fat = FAT_LFN;
#if	!MSDOS
			headbyte = -1;
#endif
			boundary = LFNENTSIZ;
			dirsize = DOSDIRENT;
			namofs = 0;
			break;
		default:
			fat = FAT_NONE;
#if	!MSDOS
			headbyte = 0;
#endif
			boundary = 4;
			dirsize = 8;	/* long + short + short */
			namofs = 0;
			break;
	}

	top = -1;
	fnamelist = (char **)Xmalloc((maxfile + 1) * sizeof(char *));
	for (i = 0; i < maxfile; i++) {
		if (isdotdir(filelist[i].name)) cp = filelist[i].name;
		else {
			if (top < 0) top = i;
			cp = convput(path, filelist[i].name,
				NULL, CVP_NEEDFILE);
		}
#ifdef	DEP_DOSEMU
		if (_dospath(cp)) cp += 2;
#endif
		fnamelist[i] = Xstrdup(cp);
	}
	fnamelist[i] = NULL;
	if (top < 0) {
		freevar(fnamelist);
		return;
	}

#ifndef	PATHNOCASE
	duppathignorecase = pathignorecase;
	pathignorecase = (fat != FAT_NONE) ? 1 : 0;
#endif
	noconv++;
	size = realdirsiz(fnamelist[top], fat, boundary, dirsize, namofs);
	len = getnamlen(size, fat, boundary, dirsize, namofs);
	if (!(tmpdir = maketmpfile(len, fat, NULL, NULL))) {
		warning(0, NOWRT_K);
		freevar(fnamelist);
#ifndef	PATHNOCASE
		pathignorecase = duppathignorecase;
#endif
		noconv--;
		return;
	}
#if	!MSDOS
	persec = getblocksize(tmpdir);
	dirblocksize = getdirblocksize(tmpdir);
#endif
	fnamp = strcatdelim2(path, tmpdir, NULL);
	waitmes();

	if (!(dirp = Xopendir(curpath))) {
		freevar(fnamelist);
#ifndef	PATHNOCASE
		pathignorecase = duppathignorecase;
#endif
		noconv--;
		lostcwd(path);
		return;
	}
	i = ent = 0;
	while ((dp = Xreaddir(dirp))) {
		if (isdotdir(dp -> d_name)) continue;
		else if (!strpathcmp2(dp -> d_name, tmpdir)) {
#if	MSDOS
			if (!(dp -> d_alias[0])) len = DOSBODYLEN;
#else	/* !MSDOS */
# ifdef	DEP_DOSDRIVE
			if (fat == FAT_DOSDRIVE
			&& wrap_reclen(dp) == DOSDIRENT)
				len = DOSBODYLEN;
# endif
#endif	/* !MSDOS */
			ent = i;
		}
		else {
			n = strcatpath(path, fnamp, dp -> d_name);
			if (n < 0 || saferename(dp -> d_name, path) < 0) {
				warning(-1, dp -> d_name);
				VOID_C Xclosedir(dirp);
				restorefile(tmpdir, path, fnamp);
				freevar(fnamelist);
#ifndef	PATHNOCASE
				pathignorecase = duppathignorecase;
#endif
				noconv--;
				return;
			}
		}
		i++;
	}
	VOID_C Xclosedir(dirp);

	if (ent > 0) {
		if (!(tmp = maketmpfile(len, fat, tmpdir, tmpdir))) {
			warning(-1, tmpdir);
			restorefile(tmpdir, path, fnamp);
			freevar(fnamelist);
#ifndef	PATHNOCASE
			pathignorecase = duppathignorecase;
#endif
			noconv--;
			return;
		}
		Xfree(tmpdir);
		tmpdir = tmp;
		fnamp = strcatdelim2(path, tmpdir, NULL);
	}

#if	!MSDOS
	if (fs != FSID_EFS) entnum = NULL;	/* except IRIX File System */
	else if (!(entnum = getentnum(curpath, persec))) {
		warning(-1, curpath);
		restorefile(tmpdir, path, fnamp);
		freevar(fnamelist);
#ifndef	PATHNOCASE
		pathignorecase = duppathignorecase;
#endif
		noconv--;
		return;
	}
	totalent = headbyte
		+ realdirsiz(tmpdir, fat, boundary, dirsize, namofs)
		+ realdirsiz(curpath, fat, boundary, dirsize, namofs)
		+ realdirsiz(parentpath, fat, boundary, dirsize, namofs);
	block = tmpno = 0;
	ptr = 3;
	totalptr = 0;
	if (entnum) totalptr = entnum[block];
	tmpfiles = NULL;
#endif	/* !MSDOS */

	for (i = 0; i < maxfile; i++) {
		if (isdotdir(fnamelist[i]) || i == top) continue;
#if	!MSDOS
		ent = dirblocksize - totalent;
		size = realdirsiz(fnamelist[i],
			fat, boundary, dirsize, namofs);
		switch (fs) {
			case FSID_EFS:	/* IRIX File System */
				if (totalptr > ptr + 1) ent -= totalptr;
				else ent -= ptr + 1;
				break;
			case FSID_SYSV:	/* SystemV R3 File System */
			case FSID_FAT:	/* MS-DOS File System */
			case FSID_LFN:	/* Windows95 File System */
				ent = size;
				break;
			default:
				break;
		}
		if (ent < size) {
			n = getnamlen(ent, fat, boundary, dirsize, namofs);
			if (n > 0) {
				tmpfiles = b_realloc(tmpfiles, tmpno, char *);
				tmpfiles[tmpno++] =
					maketmpfile(n, fat, tmpdir, NULL);
			}
			ptr = 0;
			totalent = headbyte;
			if (entnum) totalptr = entnum[++block];
		}
#endif	/* !MSDOS */
		n = strcatpath(path, fnamp, fnamelist[i]);
		if (n < 0 || saferename(path, fnamelist[i]) < 0) {
			warning(-1, path);
			break;
		}
#if	!MSDOS
		totalent += size;
		ptr++;
#endif
	}
#if	!MSDOS
	Xfree(entnum);
	if (tmpfiles) {
		for (i = 0; i < tmpno; i++) if (tmpfiles[i]) {
			if (Xunlink(tmpfiles[i]) < 0) warning(-1, tmpfiles[i]);
			Xfree(tmpfiles[i]);
		}
		Xfree(tmpfiles);
	}
#endif

	if (!(tmp = maketmpfile(len, fat, tmpdir, tmpdir)))
		warning(-1, tmpdir);
	else {
		Xfree(tmpdir);
		tmpdir = tmp;
	}
	fnamp = strcatdelim2(path, tmpdir, NULL);
	n = strcatpath(path, fnamp, fnamelist[top]);
	if (n < 0 || saferename(path, fnamelist[top]) < 0) warning(-1, path);
	restorefile(tmpdir, path, fnamp);

	freevar(fnamelist);
#ifndef	PATHNOCASE
	pathignorecase = duppathignorecase;
#endif
	noconv--;
}
#endif	/* !_NOWRITEFS */
