diff --git a/lib/modules/Stdio.pmod/module.pmod b/lib/modules/Stdio.pmod/module.pmod
index 444487a0f003ec576f30a05e39faa90ec841b83c..58867ec39695ea54fecae2efc1ef82a628475eda 100644
--- a/lib/modules/Stdio.pmod/module.pmod
+++ b/lib/modules/Stdio.pmod/module.pmod
@@ -1,4 +1,4 @@
-// $Id: module.pmod,v 1.172 2003/04/26 15:24:18 marcus Exp $
+// $Id: module.pmod,v 1.173 2003/05/15 15:23:53 marcus Exp $
 #pike __REAL_VERSION__
 
 inherit files;
@@ -211,6 +211,37 @@ class File
     return ::open(file,mode,bits);
   }
 
+#if constant(files.__HAVE_OPENPT__)
+  //! @decl int openpt(string mode)
+  //!
+  //! Open the master end of a pseudo-terminal pair.  The parameter
+  //! @[mode] should contain one or more of the following letters:
+  //! @string
+  //!   @value "r"
+  //!   Open terminal for reading.
+  //!   @value "w"
+  //!   Open terminal for writing.
+  //! @endstring
+  //!
+  //! @[mode] should always contain at least one of the letters
+  //! @expr{"r"@} or @expr{"w"@}.
+  //!
+  //! @seealso
+  //! @[grantpt()]
+  //!
+  int openpt(string mode)
+  {
+    _fd=Fd();
+    register_open_file ("pty master", open_file_id, backtrace());
+    is_file = 0;
+#ifdef __STDIO_DEBUG
+    __closed_backtrace=0;
+#endif
+    debug_file = "pty master";  debug_mode = mode; debug_bits=0;
+    return ::openpt(mode);
+  }
+#endif
+
   //! This makes this file into a socket ready for connections. The reason
   //! for this function is so that you can set the socket to nonblocking
   //! or blocking (default is blocking) before you call @[connect()].
diff --git a/src/modules/files/acconfig.h b/src/modules/files/acconfig.h
index 203b4a741d72344a8e69a289ba6760059735a3b3..e2758d11c0aaa8c9e4ebf6dfe051f7d1dac413b4 100644
--- a/src/modules/files/acconfig.h
+++ b/src/modules/files/acconfig.h
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: acconfig.h,v 1.20 2003/05/14 20:21:15 marcus Exp $
+|| $Id: acconfig.h,v 1.21 2003/05/15 15:24:06 marcus Exp $
 */
 
 #ifndef FILE_MACHINE_H
@@ -80,8 +80,11 @@
 /* Filesystem notifications */
 #undef HAVE_NOTIFICATIONS
 
-/* Defined to path of pt_chmod to use pt_chmod directly rather than
-   calling grantpt (needed on Solaris) */
+/* Define to path of pseudo terminal master device if available */
+#undef PTY_MASTER_PATHNAME
+
+/* Define to path of pt_chmod/chgpt to use pt_chmod directly rather than
+   calling grantpt (needed on SysV) */
 #undef USE_PT_CHMOD
 
 #endif
diff --git a/src/modules/files/configure.in b/src/modules/files/configure.in
index 7bd414836b926a5f63782a01f0438f79f728e34f..5aa595cc26ec9762afeb969a65f6b1e67be56d0d 100644
--- a/src/modules/files/configure.in
+++ b/src/modules/files/configure.in
@@ -1,4 +1,4 @@
-# $Id: configure.in,v 1.87 2003/05/15 13:25:41 marcus Exp $
+# $Id: configure.in,v 1.88 2003/05/15 15:24:06 marcus Exp $
 AC_INIT(file.c)
 AC_CONFIG_HEADER(file_machine.h)
 
@@ -18,8 +18,9 @@ AC_CHECK_LIB(socket, socket)
 AC_CHECK_LIB(nsl, gethostbyname)
 
 AC_HAVE_FUNCS(getwd perror readdir_r statvfs statfs ustat lseek64 lstat fsync \
- grantpt unlockpt ptsname socketpair writev sendfile munmap madvise poll \
- setsockopt getprotobyname truncate64 ftruncate64 inet_ntoa inet_ntop)
+ grantpt unlockpt ptsname posix_openpt socketpair writev sendfile munmap \
+ madvise poll setsockopt getprotobyname truncate64 ftruncate64 inet_ntoa \
+ inet_ntop)
 
 AC_FUNC_MMAP
 
@@ -1125,6 +1126,25 @@ else
   AC_MSG_RESULT(no)
 fi
 
+AC_MSG_CHECKING(for pty master device)
+AC_CACHE_VAL(pike_cv_pty_master_pathname, [
+  pike_cv_pty_master_pathname=no
+  for i in ptmx ptc; do
+    if test -c /dev/$i; then
+      pike_cv_pty_master_pathname="/dev/$i"
+      break
+    else
+      :
+    fi
+  done
+])
+AC_MSG_RESULT($pike_cv_pty_master_pathname)
+if test no = "$pike_cv_pty_master_pathname"; then
+  :
+else
+  AC_DEFINE_UNQUOTED(PTY_MASTER_PATHNAME, "$pike_cv_pty_master_pathname")
+fi
+
 if test yes = "$ac_cv_func_grantpt"; then
   pt_chmod_dirs=""
   case "$pike_cv_sys_os" in
diff --git a/src/modules/files/file.c b/src/modules/files/file.c
index 719b5c6c63ac15ffa935cc53d9952900e78894f5..ea4dc9dc58d5d7ba8b98203bf1246accf55de40a 100644
--- a/src/modules/files/file.c
+++ b/src/modules/files/file.c
@@ -2,12 +2,12 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: file.c,v 1.277 2003/05/15 13:25:59 marcus Exp $
+|| $Id: file.c,v 1.278 2003/05/15 15:24:06 marcus Exp $
 */
 
 #define NO_PIKE_SHORTHAND
 #include "global.h"
-RCSID("$Id: file.c,v 1.277 2003/05/15 13:25:59 marcus Exp $");
+RCSID("$Id: file.c,v 1.278 2003/05/15 15:24:06 marcus Exp $");
 #include "fdlib.h"
 #include "pike_netlib.h"
 #include "interpret.h"
@@ -1470,7 +1470,7 @@ static int do_close(int flags)
 
 /*! @decl string grantpt()
  *!
- *!  If this file has been created by opening /dev/ptmx, return the
+ *!  If this file has been created by calling @[openpt()], return the
  *!  filename of the associated pts-file. This function should only be
  *!  called once.
  */
@@ -1706,6 +1706,68 @@ static void file_open(INT32 args)
   push_int(fd>=0);
 }
 
+#if !defined(__NT__) && (defined(HAVE_POSIX_OPENPT) || defined(PTY_MASTER_PATHNAME))
+/*! @decl int openpt(string mode)
+ *!
+ *! Open the master end of a pseudo-terminal pair.
+ *!
+ *! @seealso
+ *!   @[grantpt()]
+ */
+static void file_openpt(INT32 args)
+{
+  int flags,fd;
+  struct pike_string *flag_str;
+  close_fd();
+
+  if(args < 1)
+    SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->openpt", 2);
+
+  if(Pike_sp[-args].type != PIKE_T_STRING)
+    SIMPLE_BAD_ARG_ERROR("Stdio.File->openpt", 1, "string");
+
+#ifdef HAVE_POSIX_OPENPT
+  flags = parse((flag_str = Pike_sp[1-args].u.string)->str);
+  
+  if(!( flags &  (FILE_READ | FILE_WRITE)))
+    Pike_error("Must open file for at least one of read and write.\n");
+
+  do {
+    THREADS_ALLOW_UID();
+    fd=posix_openpt(map(flags));
+    THREADS_DISALLOW_UID();
+    if ((fd < 0) && (errno == EINTR))
+      check_threads_etc();
+  } while(fd < 0 && errno == EINTR);
+
+  if(!Pike_fp->current_object->prog)
+  {
+    if (fd >= 0)
+      fd_close(fd);
+    Pike_error("Object destructed in Stdio.File->open()\n");
+  }
+
+  if(fd < 0)
+  {
+    ERRNO=errno;
+  }
+  else
+  {
+    init_fd(fd,flags | fd_query_properties(fd, FILE_CAPABILITIES));
+    set_close_on_exec(fd,1);
+  }
+  pop_n_elems(args);
+  push_int(fd>=0);
+#else
+  if(args > 1)
+    pop_n_elems(args - 1);
+  push_constant_text(PTY_MASTER_PATHNAME);
+  stack_swap();
+  file_open(2);
+#endif
+}
+#endif
+
 #ifdef HAVE_FSYNC
 /*! @decl int(0..1) sync()
  *!
@@ -3808,6 +3870,10 @@ PIKE_MODULE_INIT
   add_integer_constant("__HAVE_CONNECT_UNIX__",1,0);
 #endif
 
+#if !defined(__NT__) && (defined(HAVE_POSIX_OPENPT) || defined(PTY_MASTER_PATHNAME))
+  add_integer_constant("__HAVE_OPENPT__",1,0);
+#endif
+
   /* function(:array(int)) */
   ADD_FUNCTION("get_all_active_fd", f_get_all_active_fd,
 	       tFunc(tNone,tArr(tInt)), OPT_EXTERNAL_DEPEND);
diff --git a/src/modules/files/file_functions.h b/src/modules/files/file_functions.h
index c591a32d7a0382db783a5120ce689d07fb519abb..b32dedc781d43be2c944696dcd25e88036cfe463 100644
--- a/src/modules/files/file_functions.h
+++ b/src/modules/files/file_functions.h
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: file_functions.h,v 1.26 2003/04/22 15:15:50 marcus Exp $
+|| $Id: file_functions.h,v 1.27 2003/05/15 15:24:06 marcus Exp $
 */
 
 FILE_FUNC("open",file_open,"function(string,string,void|int:int)")
@@ -71,7 +71,11 @@ FILE_FUNC("open",file_open,"function(string,string,void|int:int)")
   FILE_FUNC("trylock",file_trylock,"function(void|int:object)")
 #endif
 
-#if defined(HAVE_GRANTPT)
+#if !defined(__NT__) && (defined(HAVE_POSIX_OPENPT) || defined(PTY_MASTER_PATHNAME))
+   FILE_FUNC("openpt",file_openpt,"function(string:int)")
+#endif
+
+#if defined(HAVE_GRANTPT) || defined(USE_PT_CHMOD)
    FILE_FUNC("grantpt",file_grantpt,"function(void:string)")
 #endif
 
diff --git a/src/modules/files/testsuite.in b/src/modules/files/testsuite.in
index 7a5d5898bbde5322292a9d2c4b255884bc0f902c..a4220d5e2715e5314d424f40db84983e0a12aabb 100644
--- a/src/modules/files/testsuite.in
+++ b/src/modules/files/testsuite.in
@@ -241,9 +241,18 @@ cond([[Stdio.File()->lock]],
 ]])
 
 dnl pty handling
-cond([[ Stdio.File()->grantpt ]],
+
+cond([[ Stdio.File()->openpt ]],
+[[
+    test_true(Stdio.File()->openpt("rw"));
+]])
+
+cond([[ Stdio.File()->openpt && Stdio.File()->grantpt ]],
 [[
-    test_true(stringp(Stdio.File(file_stat("/dev/ptmx")?"/dev/ptmx":"/dev/ptc")->grantpt()))
+    test_any([[
+      Stdio.File p = Stdio.File();
+      return p->openpt("rw") && stringp(p->grantpt());
+    ]], 1)
 ]])
 
 test_do(rm("conftest"))