diff --git a/src/acconfig.h b/src/acconfig.h
index 2bd41b04e63a0bca172c176f173f4a902422eb3b..9e3cb9e8d4f03c0a7103445544cef9321a0c9a4f 100644
--- a/src/acconfig.h
+++ b/src/acconfig.h
@@ -153,7 +153,14 @@
  * __chkstk. */
 #undef HAVE_BROKEN_CHKSTK
 
-/* Define if you have a working getcwd */
+/* Define if you have a working getcwd(3) (ie one that returns a malloc()ed
+ * buffer if the first argument is NULL).
+ *
+ * Define to 1 if the second argument being 0 causes getcwd(3) to allocate
+ * a buffer of suitable size (ie never fail with ERANGE).
+ *
+ * Define to 0 if the second argument MUST be > 0.
+ */
 #undef HAVE_WORKING_GETCWD
 
 /* Define for solaris */
diff --git a/src/configure.in b/src/configure.in
index b68c937692fe88cd0c0a87299050a5e748aceccc..3d5d154835c539cff1bd77d2d5f3587db1a08234 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -4705,6 +4705,82 @@ AC_DEFINE_UNQUOTED(ACCEPT_SIZE_T, $pike_cv_accept_size_t)
 AC_MSG_CHECKING(for working getcwd)
 AC_CACHE_VAL(pike_cv_func_working_getcwd,
 [
+# First try getcwd(NULL, 0).
+# This is the working == yes case, and is true on glibc and win32.
+AC_TRY_RUN([
+#ifndef _LARGEFILE_SOURCE
+#  define _FILE_OFFSET_BITS 64
+#  define _LARGEFILE_SOURCE
+#  define _LARGEFILE64_SOURCE 1
+#endif
+/* HPUX needs these too... */
+#ifndef __STDC_EXT__
+#  define __STDC_EXT__
+#endif /* !__STDC_EXT__ */
+
+#ifndef POSIX_SOURCE
+#define POSIX_SOURCE
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif /* HAVE_DIRECT_H */
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#include <signal.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __NT__
+int sig_child(int arg)
+{
+#ifdef HAVE_WAITPID
+  waitpid(-1,0,WNOHANG);
+#else
+#ifdef HAVE_WAIT3
+  wait3(0,WNOHANG,0);
+#else
+#ifdef HAVE_WAIT4
+  wait3(-1,0,WNOHANG,0);
+#else
+
+  /* Leave them hanging */
+
+#endif /* HAVE_WAIT4 */
+#endif /* HAVE_WAIT3 */
+#endif /* HAVE_WAITPID */
+
+#ifdef SIGNAL_ONESHOT
+  signal(SIGCHLD, sig_child);
+#endif
+}
+
+int sig_alarm() { exit(1); }
+#endif /* !__NT__ */
+
+int main()
+{
+  char *tmp;
+#ifndef __NT__
+  signal(SIGCHLD,sig_child);
+  signal(SIGALRM,sig_alarm);
+  alarm(4);
+#endif /* !__NT__ */
+  tmp=getcwd(0,0);
+  if(tmp && tmp[0] && (strlen(tmp) < 10000)) {
+    free(tmp);
+    exit(0);
+  }
+  exit(1);
+}
+],pike_cv_func_working_getcwd=yes,
+# The getcwd(NULL, 0) case failed.
+# Retry with getcwd(NULL, 10000).
+# This is the working == some case, and is true on Solaris.
 AC_TRY_RUN([
 #ifndef _LARGEFILE_SOURCE
 #  define _FILE_OFFSET_BITS 64
@@ -4775,12 +4851,20 @@ int main()
   }
   exit(1);
 }
-],pike_cv_func_working_getcwd=yes,pike_cv_func_working_getcwd=no,
-  pike_cv_func_working_getcwd=yes)])
+],pike_cv_func_working_getcwd=some,
+  pike_cv_func_working_getcwd=no,
+  # NB: This should be unreachable.
+  pike_cv_func_working_getcwd=no),
+  # Assume that getcwd(NULL, val) works.
+  pike_cv_func_working_getcwd=some)
+])
 
 if test "$pike_cv_func_working_getcwd" = yes; then
   AC_MSG_RESULT(yes)
-  AC_DEFINE(HAVE_WORKING_GETCWD)
+  AC_DEFINE(HAVE_WORKING_GETCWD, [1])
+elif test "$pike_cv_func_working_getcwd" = some; then
+  AC_MSG_RESULT(some)
+  AC_DEFINE(HAVE_WORKING_GETCWD, [0])
 else
   AC_MSG_RESULT(no)
 fi
diff --git a/src/fdlib.h b/src/fdlib.h
index a2988c85d75e20351afd0c57b43cc356c56808b4..076de7d8e4ea3ae826847f40bb73113f88b439db 100644
--- a/src/fdlib.h
+++ b/src/fdlib.h
@@ -387,13 +387,35 @@ static int PIKE_UNUSED_ATTRIBUTE debug_fd_mkdir(const char *dir, int mode)
 /* Glibc extension... */
 #define fd_get_current_dir_name()	get_current_dir_name()
 #elif defined(HAVE_WORKING_GETCWD)
-/* getcwd(NULL, 0) gives malloced buffer. */
+#if HAVE_WORKING_GETCWD
+/* Glibc and win32 (HAVE_WORKING_GETCWD == 1).
+ *
+ * getcwd(NULL, 0) gives malloced buffer.
+ */
 #define fd_get_current_dir_name()	getcwd(NULL, 0)
 #else
+/* Solaris-style (HAVE_WORKING_GETCWD == 0).
+ *
+ * getcwd(NULL, x) gives malloced buffer as long as x > 0, and the path fits.
+ */
 static char PIKE_UNUSED_ATTRIBUTE *debug_get_current_dir_name(void)
 {
   char *buf;
-  size_t buf_size = 1000;
+  size_t buf_size = 128;
+  do {
+    buf = getcwd(NULL, buf_size);
+    if (buf) return buf;
+    buf_size <<= 1;
+  } while (errno == ERANGE);
+  return NULL;
+}
+#define fd_get_current_dir_name()	debug_get_current_dir_name()
+#endif
+#else
+static char PIKE_UNUSED_ATTRIBUTE *debug_get_current_dir_name(void)
+{
+  char *buf;
+  size_t buf_size = 128;
   do {
     buf = malloc(buf_size);
 
@@ -406,7 +428,7 @@ static char PIKE_UNUSED_ATTRIBUTE *debug_get_current_dir_name(void)
       free(buf);
 
       if (errno == ERANGE) {
-	bufsize <<= 1;
+	buf_size <<= 1;
 	continue;
       }