- (bal) Add support for realpath and getcwd for platforms with broken
   or missing realpath implementations for sftp-server.
 - (bal) Corrected mistake in INSTALL in regards to GNU rx library
diff --git a/ChangeLog b/ChangeLog
index bbd97b9..9b37a5f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,7 @@
 20001014
+ - (bal) Add support for realpath and getcwd for platforms with broken
+   or missing realpath implementations for sftp-server.
+ - (bal) Corrected mistake in INSTALL in regards to GNU rx library
  - (bal) Add support for GNU rx library for those lacking regexp support 
  - (djm) Don't accept PAM_PROMPT_ECHO_ON messages during initial auth
  - (djm) Revert SSH2 serverloop hack, will find a better way.
diff --git a/Makefile.in b/Makefile.in
index 9969a52..a08b373 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -37,7 +37,7 @@
 
 LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o cli.o compat.o compress.o crc32.o cygwin_util.o deattack.o dispatch.o dsa.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o rijndael.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o util.o uuencode.o xmalloc.o 
 
-LIBOPENBSD_COMPAT_OBJS=bsd-arc4random.o bsd-base64.o bsd-bindresvport.o bsd-daemon.o bsd-inet_aton.o bsd-inet_ntoa.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-sigaction.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bsd-strsep.o bsd-strtok.o bsd-vis.o fake-getaddrinfo.o fake-getnameinfo.o next-posix.o
+LIBOPENBSD_COMPAT_OBJS=bsd-arc4random.o bsd-base64.o bsd-bindresvport.o bsd-daemon.o bsd-getcwd.o bsd-inet_aton.o bsd-inet_ntoa.o bsd-misc.o bsd-mktemp.o bsd-realpath.o bsd-rresvport.o bsd-setenv.o bsd-sigaction.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bsd-strsep.o bsd-strtok.o bsd-vis.o fake-getaddrinfo.o fake-getnameinfo.o next-posix.o
 
 SSHOBJS= ssh.o sshconnect.o sshconnect1.o sshconnect2.o log-client.o readconf.o clientloop.o
 
diff --git a/acconfig.h b/acconfig.h
index 3bf61ec..d212b90 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -55,6 +55,9 @@
 /* Define if you lack native POSIX regex and you are using GNU rx library */
 #undef HAVE_LIBRX
 
+/* Define if you have a broken realpath. */
+#undef BROKEN_REALPATH
+
 /* Define if you are on NeXT */
 #undef HAVE_NEXT
 
diff --git a/bsd-getcwd.c b/bsd-getcwd.c
new file mode 100644
index 0000000..cca4177
--- /dev/null
+++ b/bsd-getcwd.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 1989, 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if !defined(HAVE_GETCWD)
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char rcsid[] = "$OpenBSD: getcwd.c,v 1.6 2000/07/19 15:25:13 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/dir.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "includes.h"
+
+#define	ISDOT(dp) \
+	(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
+	    (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
+
+char *
+getcwd(char *pt,size_t size)
+{
+	register struct dirent *dp;
+	register DIR *dir = NULL;
+	register dev_t dev;
+	register ino_t ino;
+	register int first;
+	register char *bpt, *bup;
+	struct stat s;
+	dev_t root_dev;
+	ino_t root_ino;
+	size_t ptsize, upsize;
+	int save_errno;
+	char *ept, *eup, *up;
+
+	/*
+	 * If no buffer specified by the user, allocate one as necessary.
+	 * If a buffer is specified, the size has to be non-zero.  The path
+	 * is built from the end of the buffer backwards.
+	 */
+	if (pt) {
+		ptsize = 0;
+		if (!size) {
+			errno = EINVAL;
+			return (NULL);
+		}
+		ept = pt + size;
+	} else {
+		if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
+			return (NULL);
+		ept = pt + ptsize;
+	}
+	bpt = ept - 1;
+	*bpt = '\0';
+
+	/*
+	 * Allocate bytes (1024 - malloc space) for the string of "../"'s.
+	 * Should always be enough (it's 340 levels).  If it's not, allocate
+	 * as necessary.  Special * case the first stat, it's ".", not "..".
+	 */
+	if ((up = malloc(upsize = 1024 - 4)) == NULL)
+		goto err;
+	eup = up + MAXPATHLEN;
+	bup = up;
+	up[0] = '.';
+	up[1] = '\0';
+
+	/* Save root values, so know when to stop. */
+	if (stat("/", &s))
+		goto err;
+	root_dev = s.st_dev;
+	root_ino = s.st_ino;
+
+	errno = 0;			/* XXX readdir has no error return. */
+
+	for (first = 1;; first = 0) {
+		/* Stat the current level. */
+		if (lstat(up, &s))
+			goto err;
+
+		/* Save current node values. */
+		ino = s.st_ino;
+		dev = s.st_dev;
+
+		/* Check for reaching root. */
+		if (root_dev == dev && root_ino == ino) {
+			*--bpt = '/';
+			/*
+			 * It's unclear that it's a requirement to copy the
+			 * path to the beginning of the buffer, but it's always
+			 * been that way and stuff would probably break.
+			 */
+			bcopy(bpt, pt, ept - bpt);
+			free(up);
+			return (pt);
+		}
+
+		/*
+		 * Build pointer to the parent directory, allocating memory
+		 * as necessary.  Max length is 3 for "../", the largest
+		 * possible component name, plus a trailing NULL.
+		 */
+		if (bup + 3  + MAXNAMLEN + 1 >= eup) {
+			char *nup;
+
+			if ((nup = realloc(up, upsize *= 2)) == NULL)
+				goto err;
+			up = nup;
+			bup = up;
+			eup = up + upsize;
+		}
+		*bup++ = '.';
+		*bup++ = '.';
+		*bup = '\0';
+
+		/* Open and stat parent directory. 
+		 * RACE?? - replaced fstat(dirfd(dir), &s) w/ lstat(up,&s) 
+                 */
+		if (!(dir = opendir(up)) || lstat(up,&s))
+			goto err;
+
+		/* Add trailing slash for next directory. */
+		*bup++ = '/';
+
+		/*
+		 * If it's a mount point, have to stat each element because
+		 * the inode number in the directory is for the entry in the
+		 * parent directory, not the inode number of the mounted file.
+		 */
+		save_errno = 0;
+		if (s.st_dev == dev) {
+			for (;;) {
+				if (!(dp = readdir(dir)))
+					goto notfound;
+				if (dp->d_fileno == ino)
+					break;
+			}
+		} else
+			for (;;) {
+				if (!(dp = readdir(dir)))
+					goto notfound;
+				if (ISDOT(dp))
+					continue;
+				bcopy(dp->d_name, bup, dp->d_namlen + 1);
+
+				/* Save the first error for later. */
+				if (lstat(up, &s)) {
+					if (!save_errno)
+						save_errno = errno;
+					errno = 0;
+					continue;
+				}
+				if (s.st_dev == dev && s.st_ino == ino)
+					break;
+			}
+
+		/*
+		 * Check for length of the current name, preceding slash,
+		 * leading slash.
+		 */
+		if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
+			size_t len, off;
+			char *npt;
+
+			if (!ptsize) {
+				errno = ERANGE;
+				goto err;
+			}
+			off = bpt - pt;
+			len = ept - bpt;
+			if ((npt = realloc(pt, ptsize *= 2)) == NULL)
+				goto err;
+			pt = npt;
+			bpt = pt + off;
+			ept = pt + ptsize;
+			bcopy(bpt, ept - len, len);
+			bpt = ept - len;
+		}
+		if (!first)
+			*--bpt = '/';
+		bpt -= dp->d_namlen;
+		bcopy(dp->d_name, bpt, dp->d_namlen);
+		(void)closedir(dir);
+
+		/* Truncate any file name. */
+		*bup = '\0';
+	}
+
+notfound:
+	/*
+	 * If readdir set errno, use it, not any saved error; otherwise,
+	 * didn't find the current directory in its parent directory, set
+	 * errno to ENOENT.
+	 */
+	if (!errno)
+		errno = save_errno ? save_errno : ENOENT;
+	/* FALLTHROUGH */
+err:
+	if (ptsize)
+		free(pt);
+	if (up)
+		free(up);
+	if (dir)
+		(void)closedir(dir);
+	return (NULL);
+}
+
+#endif /* !defined(HAVE_GETCWD) */
diff --git a/bsd-getcwd.h b/bsd-getcwd.h
new file mode 100644
index 0000000..bee7386
--- /dev/null
+++ b/bsd-getcwd.h
@@ -0,0 +1,10 @@
+#ifndef _BSD_GETCWD_H 
+#define _BSD_GETCWD_H
+#include "config.h"
+
+#if !defined(HAVE_GETCWD)
+
+char *getcwd(char *pt, size_t size);
+
+#endif /* !defined(HAVE_GETCWD) */
+#endif /* _BSD_GETCWD_H */
diff --git a/bsd-realpath.c b/bsd-realpath.c
new file mode 100644
index 0000000..103dcc2
--- /dev/null
+++ b/bsd-realpath.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#if !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH)
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: realpath..c,v 1.4 1998/05/18 09:55:19 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * S_ISLNK compatibility
+ */
+#ifndef S_ISLNK
+#define S_ISLNK(m) ((m & 0170000) == 0120000)
+#endif
+
+/*
+ * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
+ *
+ * Find the real name of path, by removing all ".", ".." and symlink
+ * components.  Returns (resolved) on success, or (NULL) on failure,
+ * in which case the path which caused trouble is left in (resolved).
+ */
+char *
+realpath(const char *path, char *resolved)
+{
+	struct stat sb;
+	int fd, n, rootd, serrno = 0;
+	char *p, *q, wbuf[MAXPATHLEN], start[MAXPATHLEN];
+	int symlinks = 0;
+
+	/* Save the starting point. */
+	getcwd(start,MAXPATHLEN);	
+	if ((fd = open(".", O_RDONLY)) < 0) {
+		(void)strcpy(resolved, ".");
+		return (NULL);
+	}
+	close(fd);
+
+	/*
+	 * Find the dirname and basename from the path to be resolved.
+	 * Change directory to the dirname component.
+	 * lstat the basename part.
+	 *     if it is a symlink, read in the value and loop.
+	 *     if it is a directory, then change to that directory.
+	 * get the current directory name and append the basename.
+	 */
+	(void)strncpy(resolved, path, MAXPATHLEN - 1);
+	resolved[MAXPATHLEN - 1] = '\0';
+loop:
+	q = strrchr(resolved, '/');
+	if (q != NULL) {
+		p = q + 1;
+		if (q == resolved)
+			q = "/";
+		else {
+			do {
+				--q;
+			} while (q > resolved && *q == '/');
+			q[1] = '\0';
+			q = resolved;
+		}
+		if (chdir(q) < 0)
+			goto err1;
+	} else
+		p = resolved;
+
+	/* Deal with the last component. */
+	if (lstat(p, &sb) == 0) {
+		if (S_ISLNK(sb.st_mode)) {
+			if (++symlinks > MAXSYMLINKS) {
+				serrno = ELOOP;
+				goto err1;
+			}
+			n = readlink(p, resolved, MAXPATHLEN-1);
+			if (n < 0)
+				goto err1;
+			resolved[n] = '\0';
+			goto loop;
+		}
+		if (S_ISDIR(sb.st_mode)) {
+			if (chdir(p) < 0)
+				goto err1;
+			p = "";
+		}
+	}
+
+	/*
+	 * Save the last component name and get the full pathname of
+	 * the current directory.
+	 */
+	(void)strcpy(wbuf, p);
+	if (getcwd(resolved, MAXPATHLEN) == 0)
+		goto err1;
+
+	/*
+	 * Join the two strings together, ensuring that the right thing
+	 * happens if the last component is empty, or the dirname is root.
+	 */
+	if (resolved[0] == '/' && resolved[1] == '\0')
+		rootd = 1;
+	else
+		rootd = 0;
+
+	if (*wbuf) {
+		if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) {
+			serrno = ENAMETOOLONG;
+			goto err1;
+		}
+		if (rootd == 0)
+			(void)strcat(resolved, "/");
+		(void)strcat(resolved, wbuf);
+	}
+
+	/* Go back to where we came from. */
+	if (chdir(start) < 0) {
+		serrno = errno;
+		goto err2;
+	}
+	return (resolved);
+
+err1:	chdir(start);
+err2:	errno = serrno;
+	return (NULL);
+}
+#endif /* !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) */
diff --git a/bsd-realpath.h b/bsd-realpath.h
new file mode 100644
index 0000000..dc3579d
--- /dev/null
+++ b/bsd-realpath.h
@@ -0,0 +1,11 @@
+#ifndef _BSD_REALPATH_H
+#define _BSD_REALPATH_H
+
+#include "config.h"
+
+#if !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH)
+
+char *realpath(const char *path, char *resolved);
+
+#endif /* !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) */
+#endif /* _BSD_REALPATH_H */
diff --git a/configure.in b/configure.in
index a6bed75..7cd7467 100644
--- a/configure.in
+++ b/configure.in
@@ -128,6 +128,7 @@
 	conf_wtmp_location=/usr/adm/wtmp
 	MAIL=/usr/spool/mail
 	AC_DEFINE(HAVE_NEXT)
+	AC_DEFINE(BROKEN_REALPATH)
 	CFLAGS="$CFLAGS -I/usr/local/include"
 	;;
 *-*-solaris*)
@@ -269,7 +270,7 @@
 AC_CHECK_HEADERS(bstring.h endian.h floatingpoint.h getopt.h lastlog.h limits.h login.h login_cap.h maillock.h netdb.h netgroup.h netinet/in_systm.h paths.h poll.h pty.h shadow.h security/pam_appl.h sys/bitypes.h sys/bsdtty.h sys/cdefs.h sys/poll.h sys/select.h sys/stat.h sys/stropts.h sys/sysmacros.h sys/time.h sys/ttcompat.h sys/un.h stddef.h time.h ttyent.h usersec.h util.h utmp.h utmpx.h vis.h)
 
 dnl    Checks for library functions.
-AC_CHECK_FUNCS(arc4random atexit b64_ntop bcopy bindresvport_af clock fchmod freeaddrinfo futimes gai_strerror getaddrinfo getnameinfo getrusage getttyent inet_aton inet_ntoa innetgr login_getcapbool md5_crypt memmove mkdtemp on_exit openpty rresvport_af setenv seteuid setlogin setproctitle setreuid setrlimit sigaction sigvec snprintf strerror strlcat strlcpy strsep strtok_r vsnprintf vhangup vis _getpty __b64_ntop)
+AC_CHECK_FUNCS(arc4random atexit b64_ntop bcopy bindresvport_af clock fchmod freeaddrinfo futimes gai_strerror getcwd getaddrinfo getnameinfo getrusage getttyent inet_aton inet_ntoa innetgr login_getcapbool md5_crypt memmove mkdtemp on_exit openpty realpath rresvport_af setenv seteuid setlogin setproctitle setreuid setrlimit sigaction sigvec snprintf strerror strlcat strlcpy strsep strtok_r vsnprintf vhangup vis _getpty __b64_ntop)
 dnl    Checks for time functions
 AC_CHECK_FUNCS(gettimeofday time)
 dnl    Checks for libutil functions
diff --git a/openbsd-compat.h b/openbsd-compat.h
index f3ae2f3..18c67f0 100644
--- a/openbsd-compat.h
+++ b/openbsd-compat.h
@@ -6,6 +6,8 @@
 /* BSD function replacements */
 #include "bsd-arc4random.h"
 #include "bsd-bindresvport.h"
+#include "bsd-getcwd.h"
+#include "bsd-realpath.h"
 #include "bsd-rresvport.h"
 #include "bsd-misc.h"
 #include "bsd-strlcpy.h"