
/*
 * workarounds to bring the native interface close to POSIX and X/Open
 */

#define utime		______utime
#define utimes		______utimes

#include <ast.h>
#include <ast_stdio.h>
#include <error.h>
#include <tm.h>

#include "FEATURE/float"
#include "FEATURE/omitted"

#undef	OMITTED

#if _win32_botch

#define	OMITTED	1

#include <ls.h>
#include <utime.h>

#if __CYGWIN__
#include <ast_windows.h>
#include <sys/cygwin.h>
#if _win32_botch_execve || _lib_spawn_mode
#define CONVERT		1
#endif
#endif

#undef	utime
#undef	utimes

#ifndef MAX_PATH
#define MAX_PATH	PATH_MAX
#endif

/*
 * these workarounds assume each system call foo() has a _foo() entry
 * which is true for __CYGWIN__ and __EMX__ (both GNU based)
 *
 * the workarounds handle:
 *
 *	(1) .exe suffix inconsistencies
 *	(2) /bin/sh reference in execve() and spawnve()
 *	(3) bogus getpagesize() return values
 *	(4) a fork() bug that screws up shell fork()+script
 *
 * NOTE: Not all workarounds can be handled by Unix syscall intercepts.
 *	 In particular, { ksh nmake } have workarounds for case-ignorant
 *	 filesystems and { libast } has workarounds for Win32 locale info.
 */

#undef _pathconf
#undef pathconf
#undef stat

extern int		_access(const char*, int);
extern unsigned int	_alarm(unsigned int);
extern int		_chmod(const char*, mode_t);
extern int		_close(int);
extern pid_t		_execve(const char*, char* const*, char* const*);
extern uid_t		_getuid(void);
extern int		_link(const char*, const char*);
extern int		_open(const char*, int, ...);
extern long		_pathconf(const char*, int);
extern ssize_t		_read(int, void*, size_t);
extern int		_rename(const char*, const char*);
extern pid_t		_spawnve(int, const char*, char* const*, char* const*);
extern int		_stat(const char*, struct stat*);
extern int		_unlink(const char*);
extern int		_utime(const char*, const struct utimbuf*);
extern int		_utimes(const char*, const struct timeval*);
extern ssize_t		_write(int, const void*, size_t);

#if _win32_botch_access
#define sysaccess		_access
#else
#define sysaccess		access
#endif
#if _win32_botch_alarm
#define sysalarm		_alarm
#else
#define sysalarm		alarm
#endif
#if _win32_botch_chmod
#define syschmod		_chmod
#else
#define syschmod		chmod
#endif
#if _win32_botch_copy
#define sysclose		_close
#else
#define sysclose		close
#endif
#if _win32_botch_execve || _lib_spawn_mode
#define sysexecve		_execve
#else
#define sysexecve		execve
#endif
#if CONVERT
#define sysgetuid		_getuid
#else
#define sysgetuid		getuid
#endif
#if _win32_botch_link
#define syslink			_link
#else
#define syslink			link
#endif
#if _win32_botch_open || _win32_botch_copy
#define sysopen			_open
#else
#define sysopen			open
#endif
#if _win32_botch_pathconf
#define syspathconf		_pathconf
#else
#define syspathconf		pathconf
#endif
#define sysread			_read
#if _win32_botch_rename
#define sysrename		_rename
#else
#define sysrename		rename
#endif
#if _lib_spawn_mode
#define sysspawnve		_spawnve
#else
#define sysspawnve		spawnve
#endif
#if _win32_botch_stat
#define sysstat			_stat
#else
#define sysstat			stat
#endif
#if _win32_botch_truncate
#define systruncate		_truncate
#else
#define systruncate		truncate
#endif
#if _win32_botch_unlink
#define sysunlink		_unlink
#else
#define sysunlink		unlink
#endif
#if _win32_botch_utime
#define sysutime		_utime
#define sysutimes		_utimes
#else
#define sysutime		utime
#define sysutimes		utimes
#endif
#if _win32_botch_copy
#define syswrite		_write
#else
#define syswrite		write
#endif

static char*
suffix(const char* path)
{
	const char*	s = path + strlen(path);
	int		c;

	while (s > path)
		if ((c = *--s) == '.')
			return (char*)s + 1;
		else if (c == '/' || c == '\\')
			break;
	return NULL;
}

static int
execrate(const char* path, char* buf, int size, int physical)
{
	char*	s;
	int	n;
	int	oerrno;

	if (suffix(path))
		return 0;
	oerrno = errno;
	if (physical || strlen(path) >= size || !(s = pathcanon(strcpy(buf, path), size, PATH_PHYSICAL|PATH_DOTDOT|PATH_EXISTS)))
		snprintf(buf, size, "%s.exe", path);
	else if (!suffix(buf) && ((buf + size) - s) >= 4)
		strcpy(s, ".exe");
	errno = oerrno;
	return 1;
}

/*
 * return 0 if path is magic, -1 otherwise
 * ux!=0 set to 1 if path is Unix executable
 * ux!=0 also retains errno for -1 return
 */

static int
magic(const char* path, int* ux)
{
	int		fd;
	int		r;
	int		n;
	int		m;
	int		oerrno;
#if CONVERT
	unsigned char	buf[512];
#else
	unsigned char	buf[2];
#endif

	oerrno = errno;
	if ((fd = sysopen(path, O_RDONLY, 0)) >= 0)
	{
#if CONVERT
		if (ux)
			n = sizeof(buf);
		else
#endif
			n = 2;
		r = (m = sysread(fd, buf, n)) >= 2 && (buf[1] == 0x5a && (buf[0] == 0x4c || buf[0] == 0x4d) || ux && buf[0] == '#' && buf[1] == '!' && (*ux = 1) && !(ux = 0)) ? 0 : -1;
		sysclose(fd);
		if (ux)
		{
			if (r)
				oerrno = ENOEXEC;
			else if (m > 61 && (n = buf[60] | (buf[61]<<8) + 92) < (m - 1))
				*ux = (buf[n] | (buf[n+1]<<8)) == 3;
			else
				*ux = 0;
		}
	}
	else if (!ux)
		r = -1;
	else if (errno == ENOENT)
	{
		oerrno = errno;
		r = -1;
	}
	else
	{
		r = 0;
		*ux = 0;
	}
	errno = oerrno;
	return r;
}

#if _win32_botch_access

extern int
access(const char* path, int op)
{
	int	r;
	int	oerrno;
	char	buf[PATH_MAX];

	oerrno = errno;
	if ((r = sysaccess(path, op)) && errno == ENOENT && execrate(path, buf, sizeof(buf), 0))
	{
		errno = oerrno;
		r = sysaccess(buf, op);
	}
	return r;
}

#endif

#if _win32_botch_alarm

extern unsigned int
alarm(unsigned int s)
{
	unsigned int		n;
	unsigned int		r;

	static unsigned int	a;

	n = (unsigned int)time(NULL);
	if (a <= n)
		r = 0;
	else
		r = a - n;
	a = n + s - 1;
	(void)sysalarm(s);
	return r;
}

#endif

#if _win32_botch_chmod

extern int
chmod(const char* path, mode_t mode)
{
	int	r;
	int	oerrno;
	char	buf[PATH_MAX];

	if ((r = syschmod(path, mode)) && errno == ENOENT && execrate(path, buf, sizeof(buf), 0))
	{
		errno = oerrno;
		return syschmod(buf, mode);
	}
	if (!(r = syschmod(path, mode)) &&
	    (mode & (S_IXUSR|S_IXGRP|S_IXOTH)) &&
	    !suffix(path) &&
	    (strlen(path) + 4) < sizeof(buf))
	{
		oerrno = errno;
		if (!magic(path, NULL))
		{
			snprintf(buf, sizeof(buf), "%s.exe", path);
			sysrename(path, buf);
		}
		errno = oerrno;
	}
	return r;
}

#endif

#if _win32_botch_execve || _lib_spawn_mode

#if _lib_spawn_mode

/*
 * can anyone get const prototype args straight?
 */

#define execve		______execve
#define spawnve		______spawnve

#include <process.h>

#undef	execve
#undef	spawnve

#endif

#if CONVERT

/*
 * this intercept converts DOS env vars to Unix
 * we'd rather intercept main but can't twist cc to do it
 * getuid() gets ksh to do the right thing and
 * that's our main concern
 *
 *	DOSPATHVARS='a b c'	convert { a b c }
 */

static int		convertinit;

/*
 * convertvars[0] names the list of env var names
 * convertvars[i] are not converted
 */

static const char*	convertvars[] = { "DOSPATHVARS", "PATH" };

static int
convert(const char* d, const char* s)
{
	const char*	t;
	const char*	v;
	int		i;

	for (i = 0; i < elementsof(convertvars); i++)
	{
		for (v = convertvars[i], t = s; *t && *t == *v; t++, v++);
		if (*t == '=' && *v == 0)
			return 0;
	}
	for (;;)
	{
		while (*d == ' ' || *d == '\t')
			d++;
		if (!*d)
			break;
		for (t = s; *t && *t == *d; d++, t++);
		if (*t == '=' && (*d == ' ' || *d == '\t' || *d == 0))
			return t - s + 1;
		while (*d && *d != ' ' && *d != '\t')
			d++;
	}
	return 0;
}

uid_t
getuid(void)
{
	char*		d;
	char*		s;
	char*		t;
	char**		e;
	int		n;
	int		m;

	if (!convertinit++ && (d = getenv(convertvars[0])))
		for (e = environ; s = *e; e++)
			if ((n = convert(d, s)) && (m = cygwin_win32_to_posix_path_list_buf_size(s + n)) > 0)
			{
				if (!(t = malloc(n + m + 1)))
					break;
				*e = t;
				memcpy(t, s, n);
				cygwin_win32_to_posix_path_list(s + n, t + n);
			}
	return sysgetuid();
}

#endif

#ifndef _P_OVERLAY
#define _P_OVERLAY	(-1)
#endif

#define DEBUG		1

static pid_t
runve(int mode, const char* path, char* const* argv, char* const* envv)
{
	char*	s;
	char**	p;
	char**	v;

	void*		m1;
	void*		m2;
	pid_t		pid;
	int		oerrno;
	int		ux;
	int		n;
#if defined(_P_DETACH) && defined(_P_NOWAIT)
	int		pgrp;
#endif
#if CONVERT
	char*		d;
	char*		t;
	int		m;
#endif
	struct stat	st;
	char		buf[PATH_MAX];
	char		tmp[PATH_MAX];

#if DEBUG
	static int	trace;
#endif

#if defined(_P_DETACH) && defined(_P_NOWAIT)
	if (mode == _P_DETACH)
	{
		/*
		 * 2004-02-29 Cygwin _P_DETACH is useless:
		 *	spawn*() returns 0 instead of the spawned PID
		 *	spawned { pgid sid } are the same as the parent
		 */

		mode = _P_NOWAIT;
		pgrp = 1;
	}
	else
		pgrp = 0;
#endif
	if (!envv)
		envv = (char* const*)environ;
	m1 = m2 = 0;
	oerrno = errno;
#if DEBUG
	if (!trace)
		trace = (s = getenv("_AST_exec_trace")) ? *s : 'n';
#endif
	if (execrate(path, buf, sizeof(buf), 0))
	{
		if (!sysstat(buf, &st))
			path = (const char*)buf;
		else
			errno = oerrno;
	}
	if (path != (const char*)buf && sysstat(path, &st))
		return -1;
	if (!S_ISREG(st.st_mode) || !(st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
	{
		errno = EACCES;
		return -1;
	}
	if (magic(path, &ux))
	{
#if _CYGWIN_fork_works
		errno = ENOEXEC;
		return -1;
#else
		ux = 1;
		p = (char**)argv;
		while (*p++);
		if (!(v = (char**)malloc((p - (char**)argv + 2) * sizeof(char*))))
		{
			errno = EAGAIN;
			return -1;
		}
		m1 = v;
		p = v;
		*p++ = (char*)path;
		*p++ = (char*)path;
		path = (const char*)pathshell();
		if (*argv)
			argv++;
		while (*p++ = (char*)*argv++);
		argv = (char* const*)v;
#endif
	}

	/*
	 * the Win32 dll search order is
	 *	(1) the directory of path
	 *	(2) .
	 *	(3) /c/(WINNT|WINDOWS)/system32 /c/(WINNT|WINDOWS)
	 *	(4) the directories on $PATH
	 * there are no Cygwin dlls in (3), so if (1) and (2) fail
	 * to produce the required dlls it's up to (4)
	 *
	 * the standard allows PATH to be anything once the path
	 * to an executable is determined; this code ensures that PATH
	 * contains /bin so that at least the Cygwin dll, required
	 * by all Cygwin executables, will be found
	 */

	if (p = (char**)envv)
	{
		n = 1;
		while (s = *p++)
			if (strneq(s, "PATH=", 5))
			{
				s += 5;
				do
				{
					s = pathcat(s, ':', NULL, "", tmp, sizeof(tmp));
					if (streq(tmp, "/usr/bin/") || streq(tmp, "/bin/"))
					{
						n = 0;
						break;
					}
				} while (s);
				if (n)
				{
					n = 0;
					snprintf(tmp, sizeof(tmp), "%s:/bin", *(p - 1));
					*(p - 1) = tmp;
				}
				break;
			}
		if (n)
		{
			n = p - (char**)envv + 1;
			p = (char**)envv;
			if (v = (char**)malloc(n * sizeof(char*)))
			{
				m2 = v;
				envv = (char* const*)v;
				*v++ = strcpy(tmp, "PATH=/bin");
				while (*v++ = *p++);
			}
		}
#if CONVERT
		if (!ux && (d = getenv(convertvars[0])))
			for (p = (char**)envv; s = *p; p++)
				if ((n = convert(d, s)) && (m = cygwin_posix_to_win32_path_list_buf_size(s + n)) > 0)
				{
					if (!(t = malloc(n + m + 1)))
						break;
					*p = t;
					memcpy(t, s, n);
					cygwin_posix_to_win32_path_list(s + n, t + n);
				}
#endif
	}

#if DEBUG
	if (trace == 'a' || trace == 'e')
	{
		sfprintf(sfstderr, "%s %s [", mode == _P_OVERLAY ? "_execve" : "_spawnve", path);
		for (n = 0; argv[n]; n++)
			sfprintf(sfstderr, " '%s'", argv[n]);
		if (trace == 'e')
		{
			sfprintf(sfstderr, " ] [");
			for (n = 0; envv[n]; n++)
				sfprintf(sfstderr, " '%s'", envv[n]);
		}
		sfprintf(sfstderr, " ]\n");
		sfsync(sfstderr);
	}
#endif
#if _lib_spawn_mode
	if (mode != _P_OVERLAY)
	{
		pid = sysspawnve(mode, path, argv, envv);
#if defined(_P_DETACH) && defined(_P_NOWAIT)
		if (pid > 0 && pgrp)
			setpgid(pid, 0);
#endif
	}
	else
#endif
	{
#if defined(_P_DETACH) && defined(_P_NOWAIT)
		if (pgrp)
			setpgid(0, 0);
#endif
		pid = sysexecve(path, argv, envv);
	}
	if (m1)
		free(m1);
	if (m2)
		free(m2);
	return pid;
}

#if _win32_botch_execve

extern pid_t
execve(const char* path, char* const* argv, char* const* envv)
{
	return runve(_P_OVERLAY, path, argv, envv);
}

#endif

#if _lib_spawn_mode

extern pid_t
spawnve(int mode, const char* path, char* const* argv, char* const* envv)
{
	return runve(mode, path, argv, envv);
}

#endif

#endif

#if _win32_botch_getpagesize

extern size_t
getpagesize(void)
{
	return 64 * 1024;
}

#endif

#if _win32_botch_link

extern int
link(const char* fp, const char* tp)
{
	int	r;
	int	oerrno;
	char	fb[PATH_MAX];
	char	tb[PATH_MAX];

	oerrno = errno;
	if ((r = syslink(fp, tp)) && errno == ENOENT && execrate(fp, fb, sizeof(fb), 1))
	{
		if (execrate(tp, tb, sizeof(tb), 1))
			tp = tb;
		errno = oerrno;
		r = syslink(fb, tp);
	}
	return r;
}

#endif

#if _win32_botch_open || _win32_botch_copy

#if _win32_botch_copy

/*
 * this should intercept the important cases
 * dup*() and exec*() fd's will not be intercepted
 */

typedef struct Exe_test_s
{
	int		test;
	ino_t		ino;
	char		path[PATH_MAX];
} Exe_test_t;

static Exe_test_t*	exe[16];

extern int
close(int fd)
{
	int		r;
	int		oerrno;
	struct stat	st;
	char		buf[PATH_MAX];

	if (fd >= 0 && fd < elementsof(exe) && exe[fd])
	{
		r = exe[fd]->test;
		exe[fd]->test = 0;
		if (r > 0 && !fstat(fd, &st) && st.st_ino == exe[fd]->ino)
		{
			if (r = sysclose(fd))
				return r;
			oerrno = errno;
			if (!stat(exe[fd]->path, &st) && st.st_ino == exe[fd]->ino)
			{
				snprintf(buf, sizeof(buf), "%s.exe", exe[fd]->path);
				sysrename(exe[fd]->path, buf);
			}
			errno = oerrno;
			return 0;
		}
	}
	return sysclose(fd);
}

extern ssize_t
write(int fd, const void* buf, size_t n)
{
	if (fd >= 0 && fd < elementsof(exe) && exe[fd] && exe[fd]->test < 0)
		exe[fd]->test = n >= 2 && ((unsigned char*)buf)[1] == 0x5a && (((unsigned char*)buf)[0] == 0x4c || ((unsigned char*)buf)[0] == 0x4d) && !lseek(fd, 0, SEEK_CUR);
	return syswrite(fd, buf, n);
}

#endif

extern int
open(const char* path, int flags, ...)
{
	int		fd;
	int		mode;
	int		oerrno;
	char		buf[PATH_MAX];
#if _win32_botch_copy
	struct stat	st;
#endif
	va_list		ap;

	va_start(ap, flags);
	mode = (flags & O_CREAT) ? va_arg(ap, int) : 0;
	oerrno = errno;
	fd = sysopen(path, flags, mode);
#if _win32_botch_open
	if (fd < 0 && errno == ENOENT && execrate(path, buf, sizeof(buf), 0))
	{
		errno = oerrno;
		fd = sysopen(buf, flags, mode);
	}
#endif
#if _win32_botch_copy
	if (fd >= 0 && fd < elementsof(exe) && strlen(path) < PATH_MAX &&
	    (flags & (O_CREAT|O_TRUNC)) == (O_CREAT|O_TRUNC) && (mode & 0111))
	{
		if (!suffix(path) && !fstat(fd, &st) && (exe[fd] || (exe[fd] = (Exe_test_t*)malloc(sizeof(Exe_test_t)))))
		{
			exe[fd]->test = -1;
			exe[fd]->ino = st.st_ino;
			strcpy(exe[fd]->path, path);
		}
		errno = oerrno;
	}
#endif
	va_end(ap);
	return fd;
}

#endif

#if _win32_botch_pathconf

extern long
pathconf(const char* path, int op)
{
	if (sysaccess(path, F_OK))
		return -1;
	return syspathconf(path, op);
}

#endif

#if _win32_botch_rename

extern int
rename(const char* fp, const char* tp)
{
	int	r;
	int	oerrno;
	char	fb[PATH_MAX];
	char	tb[PATH_MAX];

	oerrno = errno;
	if ((r = sysrename(fp, tp)) && errno == ENOENT && execrate(fp, fb, sizeof(fb), 1))
	{
		if (execrate(tp, tb, sizeof(tb), 1))
			tp = tb;
		errno = oerrno;
		r = sysrename(fb, tp);
	}
	return r;
}

#endif

#if _win32_botch_stat

extern int
stat(const char* path, struct stat* st)
{
	int	r;
	int	oerrno;
	char	buf[PATH_MAX];

	oerrno = errno;
	if ((r = sysstat(path, st)) && errno == ENOENT && execrate(path, buf, sizeof(buf), 0))
	{
		errno = oerrno;
		r = sysstat(buf, st);
	}
	return r;
}

#endif

#if _win32_botch_truncate

extern int
truncate(const char* path, off_t offset)
{
	int	r;
	int	oerrno;
	char	buf[PATH_MAX];

	oerrno = errno;
	if ((r = systruncate(path, offset)) && errno == ENOENT && execrate(path, buf, sizeof(buf), 0))
	{
		errno = oerrno;
		r = systruncate(buf, offset);
	}
	return r;
}

#endif

#if _win32_botch_unlink

extern int
unlink(const char* path)
{
	int		r;
	int		drive;
	int		mask;
	int		suffix;
	int		stop;
	int		oerrno;
	unsigned long	base;
	char		buf[PATH_MAX];
	char		tmp[MAX_PATH];

#define DELETED_DIR_1	7
#define DELETED_DIR_2	16

	static char	deleted[] = "%c:\\temp\\.deleted\\%08x.%03x";

	static int	count = 0;

#if __CYGWIN__

	DWORD		fattr = FILE_ATTRIBUTE_NORMAL|FILE_FLAG_DELETE_ON_CLOSE;
	DWORD		share = FILE_SHARE_DELETE;
	HANDLE		hp;
	struct stat	st;
	char		nat[MAX_PATH];

	oerrno = errno;
	if (lstat(path, &st) || !S_ISREG(st.st_mode))
		goto try_unlink;
	cygwin_conv_to_full_win32_path(path, nat);
	if (!strncasecmp(nat + 1, ":\\temp\\", 7))
		goto try_unlink;
	drive = nat[0];
	path = (const char*)nat;
	for (;;)
	{
		hp = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_DELETE_ON_CLOSE, NULL);
		if (hp != INVALID_HANDLE_VALUE)
		{
			CloseHandle(hp);
			errno = oerrno;
			return 0;
		}
		if (GetLastError() != ERROR_FILE_NOT_FOUND)
			break;
		if (path == (const char*)buf || !execrate(path, buf, sizeof(buf), 1))
		{
			errno = ENOENT;
			return -1;
		}
		path = (const char*)buf;
	}
#else
	if (sysaccess(path, 0))
#if _win32_botch_access
	{
		if (errno != ENOENT || !execrate(path, buf, sizeof(buf), 1) || sysaccess(buf, 0))
			return -1;
		path = (const char*)buf;
	}
#else
		return -1;
#endif
	drive = 'C':
#endif

	/*
	 * rename to a `deleted' path just in case the file is open
	 * otherwise directory readers may choke on phantom entries
	 */

	base = ((getuid() & 0xffff) << 16) | (time(NULL) & 0xffff);
	suffix = (getpid() & 0xfff) + count++;
	snprintf(tmp, sizeof(tmp), deleted, drive, base, suffix);
	if (!sysrename(path, tmp))
	{
		path = (const char*)tmp;
		goto try_delete;
	}
	if (errno != ENOTDIR && errno != ENOENT)
		goto try_unlink;
	tmp[DELETED_DIR_2] = 0;
	if (sysaccess(tmp, 0))
	{
		mask = umask(0);
		tmp[DELETED_DIR_1] = 0;
		if (sysaccess(tmp, 0) && mkdir(tmp, S_IRWXU|S_IRWXG|S_IRWXO))
		{
			umask(mask);
			goto try_unlink;
		}
		tmp[DELETED_DIR_1] = '\\';
		r = mkdir(tmp, S_IRWXU|S_IRWXG|S_IRWXO);
		umask(mask);
		if (r)
			goto try_unlink;
		errno = 0;
	}
	tmp[DELETED_DIR_2] = '\\';
	if (!errno && !sysrename(path, tmp))
	{
		path = (const char*)tmp;
		goto try_delete;
	}
#if !__CYGWIN__
	if (errno == ENOENT)
	{
#if !_win32_botch_access
		if (execrate(path, buf, sizeof(buf), 1) && !sysrename(buf, tmp))
			path = (const char*)tmp;
#endif
		goto try_unlink;
	}
#endif
	stop = suffix;
	do
	{
		snprintf(tmp, sizeof(tmp), deleted, drive, base, suffix);
		if (!sysrename(path, tmp))
		{
			path = (const char*)tmp;
			goto try_delete;
		}
		if (++suffix > 0xfff)
			suffix = 0;
	} while (suffix != stop);
 try_delete:
#if __CYGWIN__
	hp = CreateFile(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_DELETE_ON_CLOSE, NULL);
	if (hp != INVALID_HANDLE_VALUE)
	{
		CloseHandle(hp);
		errno = oerrno;
		return 0;
	}
#endif
 try_unlink:
	errno = oerrno;
	return sysunlink(path);
}

#endif

#if _win32_botch_utime

#if __CYGWIN__

/*
 * Cygwin refuses to set st_ctime for some operations
 * this rejects that refusal
 */

static void
ctime_now(const char* path)
{
	HANDLE		hp;
	SYSTEMTIME	st;
	FILETIME	ct;
	WIN32_FIND_DATA	ff;
	struct stat	fs;
	int		oerrno;
	char		tmp[MAX_PATH];

	if (sysstat(path, &fs) || (fs.st_mode & S_IWUSR) || syschmod(path, (fs.st_mode | S_IWUSR) & S_IPERM))
		fs.st_mode = 0;
	cygwin_conv_to_win32_path(path, tmp);
	hp = CreateFile(tmp, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hp && hp != INVALID_HANDLE_VALUE)
	{
		GetSystemTime(&st);
		SystemTimeToFileTime(&st, &ct);
		SetFileTime(hp, &ct, 0, 0);
		CloseHandle(hp);
	}
	if (fs.st_mode)
		syschmod(path, fs.st_mode & S_IPERM);
	errno = oerrno;
}

#else

#define ctime_now(p)

#endif

extern int
utimes(const char* path, const struct timeval* ut)
{
	int	r;
	int	oerrno;
	char	buf[PATH_MAX];

	oerrno = errno;
	if ((r = sysutimes(path, ut)) && errno == ENOENT && execrate(path, buf, sizeof(buf), 0))
	{
		errno = oerrno;
		r = sysutimes(path = buf, ut);
	}
	if (!r)
		ctime_now(path);
	return r;
}

extern int
utime(const char* path, const struct utimbuf* ut)
{
	int	r;
	int	oerrno;
	char	buf[PATH_MAX];

	oerrno = errno;
	if ((r = sysutime(path, ut)) && errno == ENOENT && execrate(path, buf, sizeof(buf), 0))
	{
		errno = oerrno;
		r = sysutime(path = buf, ut);
	}
	if (!r)
		ctime_now(path);
	return r;
}

#endif

#endif

/*
 * some systems (Sun) miss a few functions required by their
 * own BSD-like macros
 */

#if !_lib_bzero && !defined(bzero)

void
bzero(void* b, size_t n)
{
	memset(b, 0, n);
}

#endif

#if !_lib_getpagesize || defined(getpagesize)

#ifndef OMITTED
#define OMITTED	1
#endif

#undef	getpagesize

#ifdef	_SC_PAGESIZE
#undef	_AST_PAGESIZE
#define _AST_PAGESIZE	(int)sysconf(_SC_PAGESIZE)
#else
#ifndef _AST_PAGESIZE
#define _AST_PAGESIZE	4096
#endif
#endif

int
getpagesize(void)
{
	return _AST_PAGESIZE;
}

#endif

/* Kludge to deal with GCC's overzealous optimizer breaking the pow functions.
 *
 * Removal of this will break IEEE floating point on the SVR4 platforms.
 */
#if _need_ast_pow_funs
# if _lib_powf
float (*volatile _ast_ppowf)(float,float) = &powf;
float _ast_powf(float x, float y)
{
	return x == 1.0F ? 1.0F : (*_ast_ppowf)(x,y);
}
# endif

# if _lib_pow
double (*volatile _ast_ppow)(double,double) = &pow;
double _ast_pow(double x, double y)
{
	return x == 1.0 ? 1.0 : (*_ast_ppow)(x,y);
}
# endif

# if _lib_powl
long double (*volatile _ast_ppowl)(long double,long double) = &powl;
long double _ast_powl(long double x, long double y)
{
	return x == 1.0L ? 1.0L : (*_ast_ppowl)(x,y);
}
# endif
#endif

#ifndef OMITTED

NoN(omitted)

#endif
