delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2003/03/19/07:43:21

Date: Wed, 19 Mar 2003 12:46:02 +0000
From: "Richard Dawe" <rich AT phekda DOT freeserve DOT co DOT uk>
Sender: rich AT phekda DOT freeserve DOT co DOT uk
To: djgpp-workers AT delorie DOT com
X-Mailer: Emacs 21.3.50 (via feedmail 8.3.emacs20_6 I) and Blat ver 1.8.6
Subject: fchdir, revision 2 [PATCH]
Message-Id: <E18vcu4-0000a1-00@phekda.freeserve.co.uk>
Reply-To: djgpp-workers AT delorie DOT com

Hello.

Here's revision 2 of the patch to add fchdir support to DJGPP.
I'm pretty happy with this version. The C stream I/O functions
should now work on directories. Well, they don't do anything weird
on directories. They should behave in a similar way to the POSIX I/O
functions - reads & writes fail, seeks are ignored.

OK to commit?

Bye, Rich =]

Index: include/unistd.h
===================================================================
RCS file: /cvs/djgpp/djgpp/include/unistd.h,v
retrieving revision 1.17
diff -p -u -3 -r1.17 unistd.h
--- include/unistd.h	14 Mar 2003 19:10:29 -0000	1.17
+++ include/unistd.h	19 Mar 2003 12:37:46 -0000
@@ -115,6 +115,7 @@ int		execlp(const char *_file, const cha
 int		execv(const char *_path, char *const _argv[]);
 int		execve(const char *_path, char *const _argv[], char *const _envp[]);
 int		execvp(const char *_file, char *const _argv[]);
+int		fchdir(int _fd);
 pid_t		fork(void);
 long		fpathconf(int _fildes, int _name);
 char *		getcwd(char *_buf, size_t _size);
Index: include/libc/fd_props.h
===================================================================
RCS file: /cvs/djgpp/djgpp/include/libc/fd_props.h,v
retrieving revision 1.8
diff -p -u -3 -r1.8 fd_props.h
--- include/libc/fd_props.h	4 Feb 2003 20:24:58 -0000	1.8
+++ include/libc/fd_props.h	19 Mar 2003 12:37:51 -0000
@@ -36,6 +36,9 @@ extern "C" {
 /* Set when the descriptor is opened for append only. */
 #define FILE_DESC_APPEND            0x10
 
+/* Set when the descriptor is used for directory emulation. */
+#define FILE_DESC_DIRECTORY         0x20
+
 typedef struct fd_properties fd_properties;
 
 struct fd_properties
Index: src/libc/dos/io/fd_props.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/dos/io/fd_props.txh,v
retrieving revision 1.5
diff -p -u -3 -r1.5 fd_props.txh
--- src/libc/dos/io/fd_props.txh	1 Mar 2003 11:12:37 -0000	1.5
+++ src/libc/dos/io/fd_props.txh	19 Mar 2003 12:37:52 -0000
@@ -19,6 +19,11 @@ This is an internal function that stores
 @var{fd} in a @code{fd_properties} struct.  It is called by @code{open} and
 its helper functions.
 
+The file name stored in @code{fd_properties} is the result
+of the @code{_truename} function (@pxref{_truename}) on @var{filename}.
+The @var{open_flags} are scanned and the temporary and append flags
+are stored in the @code{flags} field in @code{fd_properties}.
+
 @example
 struct fd_properties
 @{
@@ -50,6 +55,9 @@ The file descriptor is used in emulating
 
 @item FILE_DESC_APPEND
 The file pointer will be set to the end of file before each write.
+
+@item FILE_DESC_DIRECTORY
+The file descriptor is for a directory.
 
 @end table
 
Index: src/libc/posix/fcntl/open.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/posix/fcntl/open.c,v
retrieving revision 1.11
diff -p -u -3 -r1.11 open.c
--- src/libc/posix/fcntl/open.c	25 Sep 2001 00:52:30 -0000	1.11
+++ src/libc/posix/fcntl/open.c	19 Mar 2003 12:37:57 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
@@ -17,16 +18,99 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #include <io.h>
+#include <sys/fsext.h>
 
 #include <libc/dosio.h>
 #include <libc/fd_props.h>
 
+
 /* Extra share flags that can be indicated by the user */
 int __djgpp_share_flags;
 
+/* Move a file descriptor FD such that it is at least MIN_FD.
+   If the file descriptor is changed (meaning it was origially
+   *below* MIN_FD), the old one will be closed.
+   If the operation failed (no more handles available?), -1 will
+   be returned, in which case the original descriptor is still
+   valid.
+
+   This jewel is due to Morten Welinder <terra AT diku DOT dk>.  */
+static int
+move_fd (int fd, int min_fd)
+{
+  int new_fd, tmp_fd;
+
+  if (fd == -1 || fd >= min_fd)
+    return fd;
+
+  tmp_fd = dup (fd);
+  if (tmp_fd == -1)
+    return tmp_fd;
+
+  new_fd = move_fd (tmp_fd, min_fd);
+  if (new_fd != -1)
+    close (fd);		/* success--get rid of the original descriptor */
+  else
+    close (tmp_fd);	/* failure--get rid of the temporary descriptor */
+  return new_fd;
+}
+
+static int
+opendir_as_fd (const char *filename, const int oflag)
+{
+  int fd, old_fd, flags, ret;
+
+  /* Check the flags. */
+  if ((oflag & (O_RDONLY|O_WRONLY|O_RDWR)) != O_RDONLY)
+  {
+    /* Only read-only access is allowed. */
+    errno = EISDIR;
+    return -1;
+  }
+
+  /*
+   * Allocate a file descriptor that:
+   *
+   * - is dup'd off nul, so that bogus operations at least go somewhere
+   *   sensible;
+   * - is in binary mode;
+   * - is non-inheritable;
+   * - is marked as a directory.
+   *
+   * __FSEXT_alloc_fd() conveniently handles the first two. File handles
+   * greater than 19 are not inherited, due to a misfeature
+   * of the DOS exec call.
+   */
+  old_fd = __FSEXT_alloc_fd(NULL);
+  if (old_fd < 0)
+    return -1; /* Pass through errno. */
+
+  fd = move_fd(old_fd, 20);
+  if (fd < 0)
+  {
+    close(old_fd);
+    errno = EMFILE;
+    return -1;
+  }
+
+  __set_fd_properties(fd, filename, 0);
+  __set_fd_flags(fd, FILE_DESC_DIRECTORY);
+
+  flags = fcntl(fd, F_GETFD);
+  if (flags < 0)
+    return -1; /* Pass through errno. */
+  flags |= FD_CLOEXEC;
+  ret = fcntl(fd, F_SETFD, flags);
+  if (ret < 0)
+    return -1; /* Pass through errno. */
+
+  return fd;
+}
+
 int
 open(const char* filename, int oflag, ...)
 {
+  const int original_oflag = oflag;
   int fd, dmode, bintext, dont_have_share;
   char real_name[FILENAME_MAX + 1];
   int should_create = (oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
@@ -137,6 +221,12 @@ open(const char* filename, int oflag, ..
 	fd = _creat(real_name, dmode);
     }
   }
+
+  /* Is the target a directory? If so, generate a file descriptor
+   * for the directory. Skip the rest of `open', because it does not
+   * apply to directories. */
+  if ((fd == -1) && (access(real_name, D_OK) == 0))
+    return opendir_as_fd(real_name, original_oflag);
 
   if (fd == -1)
     return fd;	/* errno already set by _open or _creat */
Index: src/libc/posix/fcntl/open.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/posix/fcntl/open.txh,v
retrieving revision 1.8
diff -p -u -3 -r1.8 open.txh
--- src/libc/posix/fcntl/open.txh	29 Jan 2003 12:51:40 -0000	1.8
+++ src/libc/posix/fcntl/open.txh	19 Mar 2003 12:37:58 -0000
@@ -111,6 +111,16 @@ You can specify the share flags (a DOS s
 And you can indicate default values for the share flags in
 @code{__djgpp_share_flags}. @xref{__djgpp_share_flags}.
 
+You can open directories using @code{open}, but there is limited
+support for POSIX file operations on directories.  In particular,
+directories cannot be read using @code{read} (@pxref{read})
+or written using @code{write} (@pxref{write}).  The principal reason
+for allowing @code{open} to open directories is to support changing
+directories using @code{fchdir} (@pxref{fchdir}).  If you wish to read
+the contents of a directory, use the @code{opendir} (@pxref{opendir})
+and @code{readdir} (@pxref{readdir}) functions instead.  File descriptors
+for directories are not inherited by child programs.
+
 @subheading Return Value
 
 If successful, the file descriptor is returned.  On error, a negative
Index: src/libc/posix/fcntl/fcntl.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/posix/fcntl/fcntl.c,v
retrieving revision 1.8
diff -p -u -3 -r1.8 fcntl.c
--- src/libc/posix/fcntl/fcntl.c	11 Jun 2002 08:26:44 -0000	1.8
+++ src/libc/posix/fcntl/fcntl.c	19 Mar 2003 12:38:05 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
@@ -110,7 +111,14 @@ _fcntl_lk64(int fd, int cmd, struct floc
   offset_t pos, cur_pos, lock_pos, len;
   long long int flen;
 
-  /* First check if SHARE is loaded */
+  /* Is this a directory? If so, fail. */
+  if (__get_fd_flags(fd) & FILE_DESC_DIRECTORY)
+  {
+    errno = EINVAL;
+    return -1;
+  }
+
+  /* Check if SHARE is loaded */
   ret = _get_SHARE_status();
 
   if (!ret) /* Then SHARE is NOT loaded, just return success */
Index: src/libc/posix/fcntl/fcntl.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/posix/fcntl/fcntl.txh,v
retrieving revision 1.10
diff -p -u -3 -r1.10 fcntl.txh
--- src/libc/posix/fcntl/fcntl.txh	29 Jan 2003 12:51:40 -0000	1.10
+++ src/libc/posix/fcntl/fcntl.txh	19 Mar 2003 12:38:06 -0000
@@ -100,6 +100,8 @@ obtained, -1 is returned and @code{errno
 will be one of @code{EINVAL}, @code{EBADF}, @code{EACCES} or
 @code{ENOLCK}).
 
+Locking of directories is not supported.
+
 @item F_SETLK
 Set or clear a file segment lock according to the structure pointed to
 by the third argument.  The lock is set when @code{l_type} is
@@ -114,6 +116,8 @@ if it were @code{F_WRLCK} for a write lo
 This is because DOS/Win9x only supports one kind of lock, and it is the
 exclusive kind.
 
+Locking of directories is not supported.
+
 @item F_SETLKW
 Same as @code{F_SETLK}, but if the lock is blocked, the call will wait
 (using @code{__dpmi_yield}, see @pxref{__dpmi_yield}) until it is
@@ -121,6 +125,8 @@ unblocked and the lock can be applied.  
 exit} if the program making the call is the program which already owns
 the lock.
 
+Locking of directories is not supported.
+
 @item F_GETLK64
 @item F_SETLK64
 @item F_SETLKW64
@@ -134,6 +140,8 @@ gigabytes minus 1.  True 64-bit file loc
 The @code{struct flock64} members @var{l_start} and @var{l_len} are
 declared to be of type @code{offset_t}, which is in turn typedef'ed to
 be a @code{long long}.
+
+Locking of directories is not supported.
 @end table
 
 This function can be hooked by the @dfn{Filesystem extensions}, see
Index: src/libc/compat/unistd/lockf.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/lockf.txh,v
retrieving revision 1.3
diff -p -u -3 -r1.3 lockf.txh
--- src/libc/compat/unistd/lockf.txh	29 Jan 2003 12:44:35 -0000	1.3
+++ src/libc/compat/unistd/lockf.txh	19 Mar 2003 12:38:12 -0000
@@ -74,6 +74,7 @@ section is already locked.
 
 @item EINVAL
 Parameter @var{function} is not one of the implemented functions.
+Or: An attempt was made to lock a directory, which is not supported.
 @end table
 
 All lock requests in this implementation are coded as exclusive locks
Index: src/libc/compat/unistd/llockf.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/llockf.txh,v
retrieving revision 1.3
diff -p -u -3 -r1.3 llockf.txh
--- src/libc/compat/unistd/llockf.txh	29 Jan 2003 12:44:35 -0000	1.3
+++ src/libc/compat/unistd/llockf.txh	19 Mar 2003 12:38:12 -0000
@@ -85,6 +85,7 @@ section is already locked.
 
 @item EINVAL
 Parameter @var{function} is not one of the implemented functions.
+Or: An attempt was made to lock a directory, which is not supported.
 @end table
 
 All lock requests in this implementation are coded as exclusive locks
Index: src/libc/posix/unistd/read.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/posix/unistd/read.c,v
retrieving revision 1.4
diff -p -u -3 -r1.4 read.c
--- src/libc/posix/unistd/read.c	21 Dec 2002 22:38:18 -0000	1.4
+++ src/libc/posix/unistd/read.c	19 Mar 2003 12:38:17 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
@@ -5,9 +6,11 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <io.h>
+#include <errno.h>
 
 #include <libc/dosio.h>
 #include <libc/ttyprvt.h>
+#include <libc/fd_props.h>
 
 ssize_t
 read(int handle, void* buffer, size_t count)
@@ -21,6 +24,14 @@ read(int handle, void* buffer, size_t co
       if (__libc_read_termios_hook(handle, buffer, count, &rv) != 0)
         return rv;
     }
+
+  /* Directory? If so, fail, which indicates that the caller should use
+   * readdir() instead. */
+  if (__get_fd_flags(handle) & FILE_DESC_DIRECTORY)
+  {
+    errno = EISDIR;
+    return -1;
+  }
 
   ngot = _read(handle, buffer, count);
   if(ngot <= 0)
Index: src/libc/posix/unistd/read.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/posix/unistd/read.txh,v
retrieving revision 1.3
diff -p -u -3 -r1.3 read.txh
--- src/libc/posix/unistd/read.txh	29 Jan 2003 12:51:41 -0000	1.3
+++ src/libc/posix/unistd/read.txh	19 Mar 2003 12:38:17 -0000
@@ -15,6 +15,9 @@ This function reads at most @var{length}
 and text files, it may read less than the requested number of bytes. 
 At end-of-file, @code{read} will read exactly zero bytes.
 
+Directories cannot be read using @code{read} --- use @code{readdir}
+instead.
+
 @subheading Return Value
 
 The number of bytes read, zero meaning end-of-file, or -1 for an error. 
Index: src/libc/posix/unistd/write.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/posix/unistd/write.c,v
retrieving revision 1.10
diff -p -u -3 -r1.10 write.c
--- src/libc/posix/unistd/write.c	29 Jan 2003 01:29:42 -0000	1.10
+++ src/libc/posix/unistd/write.c	19 Mar 2003 12:38:24 -0000
@@ -40,6 +40,13 @@ write(int handle, const void* buffer, si
   if (count == 0)
     return 0; /* POSIX.1 requires this */
 
+  /* Directory? If so, fail. */
+  if (__get_fd_flags(handle) & FILE_DESC_DIRECTORY)
+  {
+    errno = EBADF;
+    return -1;
+  }
+
   if ( __has_fd_properties(handle)
   && ( __fd_properties[handle]->flags & FILE_DESC_APPEND ) )
   {
Index: src/libc/compat/unistd/llseek.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/llseek.c,v
retrieving revision 1.6
diff -p -u -3 -r1.6 llseek.c
--- src/libc/compat/unistd/llseek.c	14 Jun 2002 14:39:55 -0000	1.6
+++ src/libc/compat/unistd/llseek.c	19 Mar 2003 12:38:24 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
 /*
@@ -38,6 +39,10 @@ llseek( int handle, offset_t offset, int
   }
 
   has_props = __has_fd_properties(handle);
+
+  /* Directory? If so, we're done. */
+  if (has_props && (__fd_properties[handle]->flags & FILE_DESC_DIRECTORY))
+    return 0;
 
   /* POSIX doesn't allow seek on a pipe.  */
   if (has_props && (__fd_properties[handle]->flags & FILE_DESC_PIPE))
Index: src/libc/compat/unistd/ftruncat.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/ftruncat.c,v
retrieving revision 1.3
diff -p -u -3 -r1.3 ftruncat.c
--- src/libc/compat/unistd/ftruncat.c	4 Aug 1999 19:58:23 -0000	1.3
+++ src/libc/compat/unistd/ftruncat.c	19 Mar 2003 12:38:29 -0000
@@ -1,15 +1,26 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
 #include <unistd.h>
 #include <io.h>
+#include <errno.h>
+#include <libc/fd_props.h>
 
 int
 ftruncate(int fd, off_t where)
 {
-  off_t here = lseek(fd, 0, SEEK_CUR);
+  off_t here;
   int retval = 0;
 
+  /* Directory? If so, fail. */
+  if (__get_fd_flags(fd) & FILE_DESC_DIRECTORY)
+  {
+    errno = EINVAL;
+    return -1;
+  }
+
+  here = lseek(fd, 0, SEEK_CUR);
   if (here == -1)
     return -1;
   if (lseek(fd, where, SEEK_SET) == -1)
Index: src/libc/compat/unistd/ftruncat.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/ftruncat.txh,v
retrieving revision 1.5
diff -p -u -3 -r1.5 ftruncat.txh
--- src/libc/compat/unistd/ftruncat.txh	29 Jan 2003 12:44:35 -0000	1.5
+++ src/libc/compat/unistd/ftruncat.txh	19 Mar 2003 12:38:29 -0000
@@ -19,6 +19,8 @@ like @code{fwrite} and @code{fprintf}, s
 @code{FILE} object, you need to call @code{fflush} before calling this
 function.
 
+@code{ftruncate} does not support directories.
+
 @subheading Return Value
 
 Zero for success, nonzero for failure.
Index: src/libc/posix/sys/stat/fstat.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/posix/sys/stat/fstat.c,v
retrieving revision 1.10
diff -p -u -3 -r1.10 fstat.c
--- src/libc/posix/sys/stat/fstat.c	14 Dec 2002 12:41:15 -0000	1.10
+++ src/libc/posix/sys/stat/fstat.c	19 Mar 2003 12:38:38 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
@@ -907,6 +908,16 @@ fstat(int handle, struct stat *statbuf)
   if (func && __FSEXT_func_wrapper(func, __FSEXT_fstat, &rv, handle, statbuf))
     {
        return rv;
+    }
+
+  /* See if this is a file descriptor for a directory. If so, just
+   * use a normal stat call. */
+  if (__get_fd_flags(handle) & FILE_DESC_DIRECTORY)
+    {
+      const char *filename = __get_fd_name(handle);
+
+      if (filename)
+	return stat(filename, statbuf);
     }
 
   if (fstat_assist(handle, statbuf) == -1)
Index: src/libc/compat/time/select.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/time/select.c,v
retrieving revision 1.6
diff -p -u -3 -r1.6 select.c
--- src/libc/compat/time/select.c	14 Jun 2002 14:24:23 -0000	1.6
+++ src/libc/compat/time/select.c	19 Mar 2003 12:38:39 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
@@ -27,6 +28,8 @@
 #include <libc/ttyprvt.h>
 #include <sys/fsext.h>
 #include <libc/fsexthlp.h>
+#include <io.h>
+#include <libc/fd_props.h>
 
 inline static int
 fp_output_ready(FILE *fp)
@@ -67,9 +70,12 @@ fp_input_ready (FILE *fp)
 inline static int
 fd_output_ready(int fd)
 {
-
   __dpmi_regs regs;
 
+  /* If it's a directory, always return 0. We can't write to directories. */
+  if (__get_fd_flags(fd) & FILE_DESC_DIRECTORY)
+    return 0;
+
   regs.x.ax = 0x4407;
   regs.x.bx = fd;
   __dpmi_int (0x21, &regs);
@@ -85,24 +91,26 @@ fd_output_ready(int fd)
 inline static int
 fd_input_ready(int fd)
 {
-
   __dpmi_regs regs;
+  short dev_info;
+
+  /* If it's a directory, always return 1. That way the caller
+     will hit the EISDIR error as quickly as possible. */
+  if (__get_fd_flags(fd) & FILE_DESC_DIRECTORY)
+    return 1;
 
   /* If it's a disk file, always return 1, since DOS returns ``not ready''
      for files at EOF, but we won't block in that case.  */
-  regs.x.ax = 0x4400;
-  regs.x.bx = fd;
-  __dpmi_int (0x21, &regs);
-  if (regs.x.flags & 1)
-  {
-    errno = __doserr_to_errno (regs.x.ax);
+  dev_info = _get_dev_info(fd);
+  if (dev_info == -1)
     return -1;
-  }
-  if ((regs.x.dx & 0x80) == 0)	/* if not a character device */
+
+  if ((dev_info & _DEV_CDEV) == 0)	/* if not a character device */
     return 1;
+
   /* If it's a STDIN device, and termios buffers some characters, say
      it's ready for input.  */
-  else if ((regs.x.dx & _DEV_STDIN) == 1
+  else if ((dev_info & _DEV_STDIN) == _DEV_STDIN
 	   && __libc_read_termios_hook && __libc_termios_exist_queue ())
     return 1;
 
Index: src/libc/compat/unistd/fsync.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/fsync.c,v
retrieving revision 1.1
diff -p -u -3 -r1.1 fsync.c
--- src/libc/compat/unistd/fsync.c	12 Nov 1995 23:27:56 -0000	1.1
+++ src/libc/compat/unistd/fsync.c	19 Mar 2003 12:38:44 -0000
@@ -1,14 +1,24 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
 #include <unistd.h>
 #include <dpmi.h>
 #include <errno.h>
 #include <libc/dosio.h>
+#include <libc/fd_props.h>
 
 int
 fsync(int _fd)
 {
   int oerrno = errno;
+
+  /* Directory? If so, fail. */
+  if (__get_fd_flags(_fd) & FILE_DESC_DIRECTORY)
+  {
+    errno = EINVAL;
+    return -1;
+  }
+
   __dpmi_regs r;
   r.h.ah = 0x68;
   r.x.bx = _fd;
Index: src/libc/compat/unistd/fsync.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/fsync.txh,v
retrieving revision 1.3
diff -p -u -3 -r1.3 fsync.txh
--- src/libc/compat/unistd/fsync.txh	29 Jan 2003 12:44:35 -0000	1.3
+++ src/libc/compat/unistd/fsync.txh	19 Mar 2003 12:38:44 -0000
@@ -14,6 +14,8 @@ Forces all information about the file wi
 synchronized with the disk image.  Works by calling DOS function 0x68.
 @emph{Warning}: External disk caches are not flushed by this function.
 
+@code{ftruncate} does not support directories.
+
 @subheading Return Value
 
 Zero on success, nonzero on failure. 
Index: src/libc/posix/unistd/makefile
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/posix/unistd/makefile,v
retrieving revision 1.6
diff -p -u -3 -r1.6 makefile
--- src/libc/posix/unistd/makefile	14 Mar 2003 19:11:07 -0000	1.6
+++ src/libc/posix/unistd/makefile	19 Mar 2003 12:38:49 -0000
@@ -21,6 +21,7 @@ SRC += execv.c
 SRC += execve.c
 SRC += execvp.c
 SRC += execvpe.c
+SRC += fchdir.c
 SRC += fork.c
 SRC += fpathcon.c
 SRC += getcwd.c
Index: src/libc/ansi/stdio/fopen.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/fopen.c,v
retrieving revision 1.4
diff -p -u -3 -r1.4 fopen.c
--- src/libc/ansi/stdio/fopen.c	9 Jun 2001 10:58:58 -0000	1.4
+++ src/libc/ansi/stdio/fopen.c	19 Mar 2003 12:38:49 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
@@ -8,6 +9,7 @@
 #include <libc/file.h>
 #include <libc/local.h>
 #include <libc/dosio.h>
+#include <libc/fd_props.h>
 
 FILE *
 fopen(const char *file, const char *mode)
@@ -72,5 +74,21 @@ fopen(const char *file, const char *mode
   }
 
   f->_base = f->_ptr = NULL;
+
+  /* If this is a FILE for a directory, we need to make sure certain
+   * flags are clear and certain flags are set. Namely:
+   *
+   * - The read flag should be clear, since reads aren't allowed.
+   * - The write flag should be clear, since writes aren't allowed.
+   * - The read-write flag should be clear, because of the above.
+   * - The EOF flag should be set, so that certain functions
+   *   fail reads and writes. (Easier than modifying the functions).
+   */
+  if (__get_fd_flags(fd) & FILE_DESC_DIRECTORY)
+  {
+    f->_flag &= ~(_IORW|_IOREAD|_IOWRT);
+    f->_flag |= _IOEOF;
+  }
+
   return f;
 }
Index: src/libc/ansi/stdio/fopen.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/fopen.txh,v
retrieving revision 1.5
diff -p -u -3 -r1.5 fopen.txh
--- src/libc/ansi/stdio/fopen.txh	29 Jan 2003 12:28:44 -0000	1.5
+++ src/libc/ansi/stdio/fopen.txh	19 Mar 2003 12:38:55 -0000
@@ -69,6 +69,10 @@ object is a disk file (data.c, etc).
 If @code{b} or @code{t} is not specified in @var{mode}, the file type is
 chosen by the value of @code{fmode} (@pxref{_fmode}). 
 
+You can open directories using @code{fopen}, but there is limited
+support for stream file operations on directories.  In particular,
+they cannot be read from or written to.
+
 If you need to specify the DOS share flags use the @code{__djgpp_share_flags}.
 @xref{__djgpp_share_flags}.
 
Index: src/libc/posix/stdio/fdopen.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/posix/stdio/fdopen.c,v
retrieving revision 1.3
diff -p -u -3 -r1.3 fdopen.c
--- src/libc/posix/stdio/fdopen.c	6 Jun 2001 04:28:10 -0000	1.3
+++ src/libc/posix/stdio/fdopen.c	19 Mar 2003 12:38:55 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
@@ -60,7 +61,26 @@ fdopen(int fildes, const char *mode)
 
   f->_base = f->_ptr = NULL;
 
-  setmode(fildes, oflags & (O_TEXT|O_BINARY));
+  /* If this is a FILE for a directory, we need to make sure certain
+   * flags are clear and certain flags are set. Namely:
+   *
+   * - The read flag should be clear, since reads aren't allowed.
+   * - The write flag should be clear, since writes aren't allowed.
+   * - The read-write flag should be clear, because of the above.
+   * - The EOF flag should be set, so that certain functions
+   *   fail reads and writes. (Easier than modifying the functions).
+   */
+  if (__get_fd_flags(fildes) & FILE_DESC_DIRECTORY)
+  {
+    f->_flag &= ~(_IORW|_IOREAD|_IOWRT);
+    f->_flag |= _IOEOF;
+  }
+
+  /* If the FILE is for a directory, leave its mode alone.
+   * Otherwise, set the mode to the one requested by the caller.
+   */
+  if ((__get_fd_flags(fildes) & FILE_DESC_DIRECTORY) == 0)
+    setmode(fildes, oflags & (O_TEXT|O_BINARY));
 
   /* Set or clear the append flag according to the mode.  */
   if (__has_fd_properties(fildes))
Index: src/libc/ansi/stdio/freopen.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/freopen.c,v
retrieving revision 1.9
diff -p -u -3 -r1.9 freopen.c
--- src/libc/ansi/stdio/freopen.c	17 Oct 2002 23:00:24 -0000	1.9
+++ src/libc/ansi/stdio/freopen.c	19 Mar 2003 12:39:00 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
@@ -10,6 +11,7 @@
 #include <libc/file.h>
 #include <libc/dosio.h>
 #include <io.h>
+#include <libc/fd_props.h>
 
 FILE *
 freopen(const char *file, const char *mode, FILE *f)
@@ -75,5 +77,21 @@ freopen(const char *file, const char *mo
   }
 
   f->_base = f->_ptr = NULL;
+
+  /* If this is a FILE for a directory, we need to make sure certain
+   * flags are clear and certain flags are set. Namely:
+   *
+   * - The read flag should be clear, since reads aren't allowed.
+   * - The write flag should be clear, since writes aren't allowed.
+   * - The read-write flag should be clear, because of the above.
+   * - The EOF flag should be set, so that certain functions
+   *   fail reads and writes. (Easier than modifying the functions).
+   */
+  if (__get_fd_flags(fd) & FILE_DESC_DIRECTORY)
+  {
+    f->_flag &= ~(_IORW|_IOREAD|_IOWRT);
+    f->_flag |= _IOEOF;
+  }
+
   return f;
 }
Index: src/libc/ansi/stdio/freopen.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/freopen.txh,v
retrieving revision 1.3
diff -p -u -3 -r1.3 freopen.txh
--- src/libc/ansi/stdio/freopen.txh	29 Jan 2003 12:28:44 -0000	1.3
+++ src/libc/ansi/stdio/freopen.txh	19 Mar 2003 12:39:05 -0000
@@ -11,7 +11,8 @@ FILE *freopen(const char *filename, cons
 @subheading Description
 
 This function closes @var{file} if it was open, then opens a new
-file like @code{fopen(filename, mode)} but it reuses @var{file}.
+file like @code{fopen(filename, mode)} (@pxref{fopen})
+but it reuses @var{file}.
 
 This is useful to, for example, associate @code{stdout} with a new file. 
 
Index: src/libc/ansi/stdio/ftell.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/ftell.c,v
retrieving revision 1.2
diff -p -u -3 -r1.2 ftell.c
--- src/libc/ansi/stdio/ftell.c	20 Feb 1996 17:03:44 -0000	1.2
+++ src/libc/ansi/stdio/ftell.c	19 Mar 2003 12:39:05 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
@@ -6,12 +7,20 @@
 #include <libc/file.h>
 #include <fcntl.h>
 #include <libc/dosio.h>
+#include <libc/fd_props.h>
 
 long
 ftell(FILE *f)
 {
+  const int fd = fileno(f);
   long tres;
-  int adjust=0;
+  int adjust = 0;
+
+  /* If this is a FILE for a directory, we have no concept of position.
+   * The stream I/O functions cannot be used to read/write a FILE
+   * for directories. So, just return position 0. */
+  if (__get_fd_flags(fd) & FILE_DESC_DIRECTORY)
+    return 0;
 
   if (f->_cnt < 0)
     f->_cnt = 0;
@@ -26,7 +35,7 @@ ftell(FILE *f)
   }
   else
     return -1;
-  tres = lseek(fileno(f), 0L, 1);
+  tres = lseek(fd, 0L, 1);
   if (tres<0)
     return tres;
   tres += adjust;
Index: src/libc/ansi/stdio/fseek.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/fseek.c,v
retrieving revision 1.3
diff -p -u -3 -r1.3 fseek.c
--- src/libc/ansi/stdio/fseek.c	8 Sep 1997 23:19:32 -0000	1.3
+++ src/libc/ansi/stdio/fseek.c	19 Mar 2003 12:39:12 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
@@ -7,12 +8,20 @@
 #include <libc/file.h>
 #include <fcntl.h>
 #include <libc/dosio.h>
+#include <libc/fd_props.h>
 
 int
 fseek(FILE *f, long offset, int ptrname)
 {
+  const int fd = fileno(f);
   long p = -1;			/* can't happen? */
 
+  /* If this is a FILE for a directory, we have no concept of position.
+   * The stream I/O functions cannot be used to read/write a FILE
+   * for directories. So, just return position 0. */
+  if (__get_fd_flags(fd) & FILE_DESC_DIRECTORY)
+    return 0;
+
   /* See comment in filbuf.c */
   f->_fillsize = 512;
 
@@ -41,7 +50,7 @@ fseek(FILE *f, long offset, int ptrname)
     if (f->_flag & _IORW)
       f->_flag &= ~_IOREAD;
 
-    p = lseek(fileno(f), offset, ptrname);
+    p = lseek(fd, offset, ptrname);
     f->_cnt = 0;
     f->_ptr = f->_base;
     f->_flag &= ~_IOUNGETC;
@@ -49,7 +58,7 @@ fseek(FILE *f, long offset, int ptrname)
   else if (f->_flag & (_IOWRT|_IORW))
   {
     p = fflush(f);
-    return lseek(fileno(f), offset, ptrname) == -1 || p == EOF ?
+    return lseek(fd, offset, ptrname) == -1 || p == EOF ?
       -1 : 0;
   }
   return p==-1 ? -1 : 0;
Index: src/libc/ansi/stdio/rewind.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/rewind.c,v
retrieving revision 1.2
diff -p -u -3 -r1.2 rewind.c
--- src/libc/ansi/stdio/rewind.c	28 Jun 1998 23:49:00 -0000	1.2
+++ src/libc/ansi/stdio/rewind.c	19 Mar 2003 12:39:16 -0000
@@ -1,14 +1,23 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1994 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <libc/file.h>
+#include <libc/fd_props.h>
 
 void rewind(FILE *f)
 {
+  const int fd = fileno(f);
+
+  /* If this is a FILE for a directory, we must maintain its EOF flag.
+   * Just return. */
+  if (__get_fd_flags(fd) & FILE_DESC_DIRECTORY)
+    return;
+
   fflush(f);
-  lseek(fileno(f), 0L, SEEK_SET);
+  lseek(fd, 0L, SEEK_SET);
   f->_fillsize = 512;	/* See comment in filbuf.c */
   f->_cnt = 0;
   f->_ptr = f->_base;
Index: src/libc/ansi/stdio/fclose.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/fclose.c,v
retrieving revision 1.1
diff -p -u -3 -r1.1 fclose.c
--- src/libc/ansi/stdio/fclose.c	25 Jul 1995 08:22:34 -0000	1.1
+++ src/libc/ansi/stdio/fclose.c	19 Mar 2003 12:39:21 -0000
@@ -1,3 +1,4 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
 #include <stdio.h>
@@ -6,30 +7,36 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <libc/file.h>
+#include <libc/fd_props.h>
 
 int
 fclose(FILE *f)
 {
-  int r;
+  const int fd = fileno(f);
+  int r = EOF;
 
-  r = EOF;
   if (!f)
     return r;
-  if (f->_flag & (_IOREAD|_IOWRT|_IORW)
-      && !(f->_flag&_IOSTRG))
+
+  /* A FILE for a directory won't have any of the read or write flags
+   * set. But we still want to tidy it up. */
+  if (   (f->_flag & (_IOREAD|_IOWRT|_IORW) && !(f->_flag&_IOSTRG))
+      || (__get_fd_flags(fd) & FILE_DESC_DIRECTORY))
   {
     r = fflush(f);
-    if (close(fileno(f)) < 0)
+    if (close(fd) < 0)
       r = EOF;
     if (f->_flag&_IOMYBUF)
       free(f->_base);
   }
+
   if (f->_flag & _IORMONCL && f->_name_to_remove)
   {
     remove(f->_name_to_remove);
     free(f->_name_to_remove);
     f->_name_to_remove = 0;
   }
+
   f->_cnt = 0;
   f->_base = 0;
   f->_ptr = 0;
Index: tests/libc/posix/unistd/makefile
===================================================================
RCS file: /cvs/djgpp/djgpp/tests/libc/posix/unistd/makefile,v
retrieving revision 1.5
diff -p -u -3 -r1.5 makefile
--- tests/libc/posix/unistd/makefile	14 Mar 2003 19:12:07 -0000	1.5
+++ tests/libc/posix/unistd/makefile	19 Mar 2003 12:39:26 -0000
@@ -8,8 +8,10 @@ SRC += getpid.c
 SRC += sleep.c
 SRC += tread.c
 SRC += write.c
+SRC += t-fchdir.c
 SRC += t-isatty.c
 SRC += t-lseek.c
 SRC += t-pwrite.c
+SRC += t-select.c
 
 include $(TOP)/../makefile.inc
--- /dev/null	2003-03-19 12:43:40.000000000 +0000
+++ src/libc/posix/unistd/fchdir.c	2003-03-08 13:06:04.000000000 +0000
@@ -0,0 +1,26 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
+#include <libc/stubs.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <io.h>
+#include <libc/fd_props.h>
+
+int
+fchdir (int fd)
+{
+  const char *filename = __get_fd_name(fd);
+  const int   flags    = __get_fd_flags(fd);
+
+  /* Check that it's a valid file descriptor. */
+  if (_get_dev_info(fd) == -1)
+    return(-1);
+
+  /* Is it actually a directory? */
+  if (((flags & FILE_DESC_DIRECTORY) == 0) || (filename == NULL)) {
+    errno = ENOTDIR;
+    return(-1);
+  }
+
+  return(chdir(filename));
+}
--- /dev/null	2003-03-19 12:43:40.000000000 +0000
+++ src/libc/posix/unistd/fchdir.txh	2003-03-09 15:59:58.000000000 +0000
@@ -0,0 +1,28 @@
+@node fchdir, file system
+@findex fchdir
+@subheading Syntax
+
+@example
+#include <unistd.h>
+
+int fchdir(int fd);
+@end example
+
+@subheading Description
+
+This function changes the current directory to the directory
+described by the file descriptor @var{fd}.
+
+@subheading Return Value
+
+Zero on success, else nonzero and @var{errno} set if error.
+
+@subheading Portability
+
+@portability !ansi, posix
+
+@subheading Example
+
+@example
+TODO
+@end example
--- /dev/null	2003-03-19 12:43:40.000000000 +0000
+++ tests/libc/posix/unistd/t-fchdir.c	2003-03-19 12:34:10.000000000 +0000
@@ -0,0 +1,638 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <limits.h>
+#include <libc/fd_props.h>
+
+#define TEST_DIR "t-fchdir.dir"
+#define TEST_FILE "t-fchdir.fil"
+
+static inline void
+fail (void)
+{
+  puts("FAIL");
+  exit(EXIT_FAILURE);
+}
+
+int
+main (int argc, char *argv[])
+{
+  off_t offset;
+  char buf[1024], buf2[1024];
+  char cwd[PATH_MAX];
+  fd_set read_fds, write_fds, except_fds;
+  short dev_info;
+  struct stat stat_buf, fstat_buf;
+  int fd, ret;
+  FILE *fp;
+  fpos_t fpos;
+  long l;
+  char *p;
+
+  if (   (access(TEST_DIR, D_OK) != 0)
+      && (mkdir(TEST_DIR, S_IRUSR|S_IWUSR) < 0))
+    {
+      perror(argv[0]);
+      fail();
+    }
+
+  /* --- Regular file testing --- */
+
+  /* Check we haven't broken I/O for normal files. */
+
+  /* - POSIX I/O functions - */
+
+  if (!access(TEST_FILE, F_OK) && (unlink(TEST_FILE) < 0))
+    {
+      puts("'" TEST_FILE "' is in the way - please remove it.");
+      fail();
+    }
+
+  fd = open(TEST_FILE, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+  if (fd < 0)
+    {
+      puts("Unable to open() '" TEST_FILE "'!");
+      fail();
+    }
+
+  assert(sizeof(buf) == sizeof(buf2));
+  memset(buf, 'X', sizeof(buf));
+  memset(buf2, ' ', sizeof(buf2));
+
+  ret = write(fd, buf, sizeof(buf));
+  if (ret < 0)
+    {
+      puts("Unable to write() '" TEST_FILE "'!");
+      fail();
+    }
+
+  ret = lseek(fd, 0, SEEK_SET);
+  if (ret < 0)
+    {
+      puts("Unable to lseek() '" TEST_FILE "'!");
+      fail();
+    }
+
+  ret = read(fd, buf2, sizeof(buf2));
+  if (ret < 0)
+    {
+      puts("Unable to read() '" TEST_FILE "'!");
+      fail();
+    }
+
+  if (memcmp(buf, buf2, sizeof(buf)) != 0)
+    {
+      puts("Differences found in comparison of data read() "
+	   "after write() on '" TEST_FILE "'!");
+      fail();
+    }
+
+  ret = close(fd);
+  if (ret < 0)
+    {
+      puts("Unable to close '" TEST_FILE "'!");
+      perror(argv[0]);
+      fail();
+    }
+
+  /* - C stream I/O functions - */
+
+  fp = fopen(TEST_FILE, "r+b");
+  if (fp == NULL)
+    {
+      puts("Unable to fopen() '" TEST_FILE "' for read-write & append!");
+      fail();
+    }
+
+  memset(buf2, ' ', sizeof(buf2));
+
+  if (fread(buf2, sizeof(buf2), 1, fp) != 1)
+    {
+      puts("Unable to fread() '" TEST_FILE "'!");
+      fail();
+    }
+
+  if (memcmp(buf, buf2, sizeof(buf)) != 0)
+    {
+      puts("Differences found in comparison of data fread() "
+	   "after write() on '" TEST_FILE "'!");
+      fail();
+    }
+
+  if (fseek(fp, sizeof(buf), SEEK_SET))
+    {
+      puts("Unable to fseek() '" TEST_FILE "'!");
+      fail();
+    }
+
+  if (fwrite(buf, sizeof(buf), 1, fp) != 1)
+    {
+      puts("Unable to fwrite() '" TEST_FILE "'!");
+      fail();
+    }
+
+  if (fseek(fp, sizeof(buf), SEEK_SET))
+    {
+      puts("Unable to fseek() '" TEST_FILE "'!");
+      fail();
+    }
+
+  if (fread(buf2, sizeof(buf2), 1, fp) != 1)
+    {
+      puts("Unable to fread() '" TEST_FILE "'!");
+      fail();
+    }
+
+  if (memcmp(buf, buf2, sizeof(buf)) != 0)
+    {
+      puts("Differences found in comparison of data fread() "
+	   "after fwrite() on '" TEST_FILE "'!");
+      fail();
+    }
+
+  if (fclose(fp) == EOF)
+    {
+      puts("Unable to fclose() '" TEST_FILE "'!");
+      fail();
+    }
+
+  /* --- Directory testing --- */
+
+  /* - POSIX I/O functions - */
+
+  /* Try opening the directory for writes. */
+  fd = open(TEST_DIR, O_RDWR);
+  if ((fd >= 0) || ((fd < 0) && (errno != EISDIR)))
+    {
+      if (fd >= 0)
+	puts("Unexpectedly able to open() '" TEST_DIR "' for read-write!");
+      else
+	perror(argv[0]);
+
+      fail();
+    }
+
+  fd = open(TEST_DIR, O_WRONLY);
+  if ((fd >= 0) || ((fd < 0) && (errno != EISDIR)))
+    {
+      if (fd >= 0)
+	puts("Unexpectedly able to open() '" TEST_DIR "' for write-only!");
+      else
+	perror(argv[0]);
+
+      fail();
+    }
+
+  /* Try opening the file for reads. */
+  fd = open(TEST_DIR, O_RDONLY);
+  if (fd < 0)
+    {
+      puts("Unable to open() '" TEST_DIR "'!");
+      perror(argv[0]);
+      fail();
+    }
+
+  /* Try read, write. */
+  memset(buf, 'X', sizeof(buf));
+
+  ret = read(fd, buf, sizeof(buf));
+  if ((ret >= 0) || ((ret < 0) && (errno != EISDIR)))
+    {
+      if (ret >= 0)
+	puts("Unexpectedly able to read() from '" TEST_DIR "'!");
+      else
+	perror(argv[0]);
+
+      fail();
+    }
+
+  ret = write(fd, buf, sizeof(buf));
+  if ((ret >= 0) || ((ret < 0) && (errno != EBADF)))
+    {
+      if (ret >= 0)
+	puts("Unexpectedly able to write() to '" TEST_DIR "'!");
+      else
+	perror(argv[0]);
+
+      fail();
+    }
+
+  /* Try lseek. */
+  offset = lseek(fd, 42L, SEEK_SET);
+  if (offset != 0)
+    {
+      if (offset != -1)
+	printf("Unexpected result from lseek() on '%s': %ld\n",
+	       TEST_DIR, (long) offset);
+      else
+	perror(argv[0]);
+
+      fail();
+    }
+
+  /* Try ftruncate. */
+  ret = ftruncate(fd, 20L);
+  if ((ret != -1) || ((ret == -1) && (errno != EINVAL)))
+    {
+      if (ret != -1)
+	puts("Unexpectedly able to ftruncate() '" TEST_DIR "'!");
+      else
+	perror(argv[0]);
+
+      fail();
+    }
+
+  /* Try fchmod. Make the directory read-only. */
+  ret = fchmod(fd, S_IRUSR);
+  if (ret < 0)
+    {
+      perror(argv[0]);
+      fail();
+    }
+
+  /* Try fchown. */
+  ret = fchown(fd, getuid() * 2, getgid() * 2);
+  if (ret < 0)
+    {
+      perror(argv[0]);
+      fail();
+    }
+
+  /* Try ioctl. Check that we get the same device info as _get_dev_info. */
+  dev_info = _get_dev_info(fd);
+  if (dev_info == -1)
+    {
+      puts("Unable to get device information for '" TEST_DIR "'!");
+      perror(argv[0]);
+      fail();
+    }
+
+  ret = ioctl(fd, DOS_GETDEVDATA);
+  if ((ret == -1) || ((ret & 0xffff) != (dev_info & 0xffff)))
+    {
+      if (ret != dev_info)
+	{
+	  puts("Different device information from ioctl() for '"
+	       TEST_DIR "'!");
+	  printf("_get_dev_info() vs. ioctl(): %d vs. %d\n",
+		 dev_info, ret);
+	}
+      else
+	{
+	  perror(argv[0]);
+	}
+
+      fail();
+    }
+
+  /* Try fcntl. Check that the close-on-exec flag is set. */
+  ret = fcntl(fd, F_GETFD);
+  if ((ret < 0) || ((ret & FD_CLOEXEC) == 0))
+    {
+      if (ret >= 0)
+	puts("FD_CLOEXEC was not set for '" TEST_DIR "'!");
+      else
+	perror(argv[0]);
+
+      fail();
+    }
+
+  /* Try lockf. */
+  ret = lockf(fd, F_TLOCK, 1024);
+  if ((ret >= 0) || ((ret < 0) && (errno != EINVAL)))
+    {
+      if (ret >= 0)
+	puts("Unexpectedly able to lockf() '" TEST_DIR "'!");
+      else
+	perror(argv[0]);
+
+      fail();
+    }
+
+  /* Try fstat. Compare the result with that of stat. */
+  ret = fstat(fd, &fstat_buf);
+  if (ret < 0)
+    {
+      puts("fstat() failed!");
+      perror(argv[0]);
+      fail();
+    }
+
+  ret = stat(TEST_DIR, &stat_buf);
+  if (ret < 0)
+    {
+      puts("stat() failed!");
+      perror(argv[0]);
+      fail();
+    }
+
+  if (memcmp(&stat_buf, &fstat_buf, sizeof(stat_buf)) != 0)
+    {
+      puts("fstat() and stat() returned different results!");
+      fail();
+    }
+
+  if (!S_ISDIR(stat_buf.st_mode))
+    {
+      puts("fstat() doesn't think '" TEST_DIR "' is a directory!");
+      fail();
+    }
+
+  /* Copied & modified from src/libc/posix/sys/stat/stat.c. */
+  printf("%s: %d %6u %o %d %d %ld %lu %s", __get_fd_name(fd),
+	 stat_buf.st_dev,
+	 (unsigned)stat_buf.st_ino,
+	 stat_buf.st_mode,
+	 stat_buf.st_nlink,
+	 stat_buf.st_uid,
+	 (long)stat_buf.st_size,
+	 (unsigned long)stat_buf.st_mtime,
+	 ctime(&stat_buf.st_mtime));
+
+  printf("\t\t\tBlock size: %d\n",
+	 stat_buf.st_blksize);
+
+  _djstat_describe_lossage(stderr);
+
+  /* Try select. */
+  FD_ZERO(&read_fds);
+  FD_ZERO(&write_fds);
+  FD_ZERO(&except_fds);
+
+  FD_SET(fd, &read_fds);
+  FD_SET(fd, &write_fds);
+  FD_SET(fd, &except_fds);
+
+  ret = select(fd + 1, &read_fds, &write_fds, &except_fds, NULL);
+  if (ret < 0)
+    {
+      puts("select() failed!");
+      perror(argv[0]);
+      fail();
+    }
+
+  if (ret != 1)
+    {
+      puts("select() did not return the expected number!");
+      fail();
+    }
+
+  if (!FD_ISSET(fd, &read_fds))
+    {
+      puts("Directory '" TEST_DIR "' should be ready for reading!");
+      fail();
+    }
+
+  if (FD_ISSET(fd, &write_fds))
+    {
+      puts("Directory '" TEST_DIR "' should not be ready for writing!");
+      fail();
+    }
+
+  if (FD_ISSET(fd, &except_fds))
+    {
+      puts("Directory '" TEST_DIR "' should not have an error!");
+      fail();
+    }
+
+  /* Try fsync. */
+  ret = fsync(fd);
+  if ((ret >= 0) || ((ret < 0) && (errno != EINVAL)))
+    {
+      if (ret >= 0)
+	puts("Unexpectedly able to fsync() to '" TEST_DIR "'!");
+      else
+	perror(argv[0]);
+
+      fail();
+    }
+
+  /* Try fchdir. */
+  ret = fchdir(fd);
+  if (ret < 0)
+    {
+      perror(argv[0]);
+      fail();
+    }
+
+  /* Where are we? */
+  if (getcwd(cwd, sizeof(cwd)) == NULL)
+    {
+      perror(argv[0]);
+      fail();
+    }
+
+  p = basename(cwd);
+  if (p == NULL)
+    {
+      printf("basename() failed on '%s'\n", cwd);
+      fail();
+    }
+
+  if (strcasecmp(p, TEST_DIR) != 0)
+    {
+      printf("Now in '%s'; expected to be in '%s'\n", p, TEST_DIR);
+      fail();
+    }
+
+  /* Change back. */
+  ret = chdir("..");
+  if (ret < 0)
+    {
+      perror(argv[0]);
+      fail();
+    }
+
+  /* Done. */
+  ret = close(fd);
+  if (ret < 0)
+    {
+      puts("Unable to close '" TEST_DIR "'!");
+      perror(argv[0]);
+      fail();
+    }
+
+  /* - C stream I/O functions - */
+
+  /* Try various flags on fopen. */
+  fp = fopen(TEST_DIR, "wt");
+  if (fp != NULL)
+    {
+      puts("Unexpectedly able to fopen() '" TEST_DIR "' for writing!");
+      fail();
+    }
+
+  fp = fopen(TEST_DIR, "w+t");
+  if (fp != NULL)
+    {
+      puts("Unexpectedly able to fopen() '" TEST_DIR "' for read & writing!");
+      fail();
+    }
+
+  fp = fopen(TEST_DIR, "at");
+  if (fp != NULL)
+    {
+      puts("Unexpectedly able to fopen() '" TEST_DIR "' for read & writing!");
+      fail();
+    }
+
+  fp = fopen(TEST_DIR, "a+t");
+  if (fp != NULL)
+    {
+      puts("Unexpectedly able to fopen() '" TEST_DIR "' for read & writing!");
+      fail();
+    }
+
+  fp = fopen(TEST_DIR, "rt");
+  if (fp == NULL)
+    {
+      puts("Unable to fopen() '" TEST_DIR "' for reading!");
+      fail();
+    }
+
+  /* Try fread, getc, fgetc, fgets. */
+  if (fread(buf, sizeof(buf), 1, fp) != 0)
+    {
+      puts("Unexpectedly able to fread() '" TEST_DIR "'!");
+      fail();
+    }
+
+  if (getc(fp) != EOF)
+    {
+      puts("Unexpectedly able to getc() '" TEST_DIR "'!");
+      fail();
+    }
+
+  if (fgetc(fp) != EOF)
+    {
+      puts("Unexpectedly able to fgetc() '" TEST_DIR "'!");
+      fail();
+    }
+
+  if (fgets(buf, sizeof(buf), fp) != NULL)
+    {
+      puts("Unexpectedly able to fgets() '" TEST_DIR "'!");
+      fail();
+    }
+
+  /* Try ungetc. */
+  if (ungetc('X', fp) != EOF)
+    {
+      puts("Unexpectedly able to ungetc() '" TEST_DIR "'!");
+      fail();
+    }
+
+  /* Try fwrite, putc, fputc, fputs. */
+  memset(buf, 'X', sizeof(buf) - 1);
+  buf[sizeof(buf) - 1] = '\0';
+
+  if (fwrite(buf, sizeof(buf), 1, fp) != 0)
+    {
+      puts("Unexpectedly able to fwrite() '" TEST_DIR "'!");
+      fail();
+    }
+
+  if (fputc('X', fp) != EOF)
+    {
+      puts("Unexpectedly able to fputc() '" TEST_DIR "'!");
+      fail();
+    }
+
+  if (fputs(buf, fp) != EOF)
+    {
+      puts("Unexpectedly able to fputs() '" TEST_DIR "'!");
+      fail();
+    }
+
+  /* Try fgetpos, ftell. */
+  if (fgetpos(fp, &fpos))
+    {
+      puts("Unable to fgetpos() '" TEST_DIR "'!");
+      fail();
+    }
+
+  l = ftell(fp);
+
+  if (l == -1)
+    {
+      puts("Unable to ftell() '" TEST_DIR "'!");
+      fail();
+    }
+
+  if (l != 0)
+    {
+      puts("Unexpected non-zero offset from ftell() '" TEST_DIR "'!");
+      fail();
+    }
+
+  /* Try fsetpos, fseek. */
+  fpos = 1024;
+
+  if (fsetpos(fp, &fpos))
+    {
+      puts("Unable to fsetpos() '" TEST_DIR "'!");
+      fail();
+    }
+
+  if (fseek(fp, 1024, SEEK_SET))
+    {
+      puts("Unable to fseek() '" TEST_DIR "'!");
+      fail();
+    }
+
+  /* Try rewind. */
+  rewind(fp);
+
+  /* Try fgetc, fputc again, to check rewind hasn't removed
+   * the EOF condition. */
+  if (fgetc(fp) != EOF)
+    {
+      puts("Unexpectedly able to fgetc() '" TEST_DIR "' after rewind!");
+      fail();
+    }
+
+  if (fputc('X', fp) != EOF)
+    {
+      puts("Unexpectedly able to fputc() '" TEST_DIR "' after rewind!");
+      fail();
+    }
+
+  /* Check that we're at EOF. */
+  if (!feof(fp))
+    {
+      puts("'" TEST_DIR "' should always be at EOF!");
+      fail();
+    }
+
+  /* Check that there are no errors. */
+  if (ferror(fp))
+    {
+      puts("Unexpected error for '" TEST_DIR "'!");
+      fail();
+    }
+
+  /* Try fpurge. */
+  if (fpurge(fp))
+    {
+      puts("Unable to fpurge() '" TEST_DIR "'!");
+      fail();
+    }
+
+  /* Try fclose. */
+  if (fclose(fp) == EOF)
+    {
+      puts("Unable to fclose() '" TEST_DIR "'!");
+      fail();
+    }
+
+  puts("PASS");
+  return(EXIT_SUCCESS);
+}
--- /dev/null	2003-03-19 12:43:40.000000000 +0000
+++ tests/libc/posix/unistd/t-select.c	2003-03-10 21:58:22.000000000 +0000
@@ -0,0 +1,187 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define TEST_FILE "t-select.tst"
+
+int
+main (int argc, char *argv[])
+{
+  fd_set read_fds;
+  fd_set write_fds;
+  fd_set except_fds;
+  struct timeval tv;
+  int fd, ret;
+
+  /* Create the test file, if necessary. */
+  if (access(TEST_FILE, D_OK) == 0)
+    {
+      puts("Directory in way - please remove '" TEST_FILE "'!");
+      puts("FAIL");
+      return(EXIT_SUCCESS);
+    }
+
+  if (access(TEST_FILE, F_OK) != 0)
+    {
+      fd = open(TEST_FILE, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
+      if (fd < 0)
+	puts("Unable to create '" TEST_FILE "'!");
+    }
+  else
+    {
+      fd = open(TEST_FILE, O_RDWR);
+      if (fd < 0)
+	puts("Unable to open '" TEST_FILE "'!");
+    }
+
+  if (fd < 0)
+    {
+      perror(argv[0]);
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  /* Check the file is ready for read & write and that there aren't
+   * any errors. */
+  FD_ZERO(&read_fds);
+  FD_ZERO(&write_fds);
+  FD_ZERO(&except_fds);
+
+  FD_SET(fd, &read_fds);
+  FD_SET(fd, &write_fds);
+  FD_SET(fd, &except_fds);
+
+  ret = select(fd + 1, &read_fds, &write_fds, &except_fds, NULL);
+  if (ret < 0)
+    {
+      perror(argv[0]);
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  if (ret != 2)
+    {
+      puts("select() did not return the expected number!");
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  if (!FD_ISSET(fd, &read_fds))
+    {
+      puts("File '" TEST_FILE "' should be ready for reading!");
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  if (!FD_ISSET(fd, &write_fds))
+    {
+      puts("File '" TEST_FILE "' should be ready for writing!");
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  if (FD_ISSET(fd, &except_fds))
+    {
+      puts("File '" TEST_FILE "' should not have an error!");
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  /* Try a time-out. */
+  FD_ZERO(&except_fds);
+
+  FD_SET(fd, &except_fds);
+
+  tv.tv_sec  = 5;
+  tv.tv_usec = 0;
+  printf("Trying a time-out: %ld seconds\n", (long) tv.tv_sec);
+
+  ret = select(fd + 1, NULL, NULL, &except_fds, &tv);
+  if (ret < 0)
+    {
+      perror(argv[0]);
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  if (FD_ISSET(fd, &except_fds))
+    {
+      puts("File '" TEST_FILE "' should not have an error!");
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  /* Done with the file now. */
+  close(fd);
+  fd = -1;
+
+  /* Try select on stdin. */
+  FD_ZERO(&read_fds);
+
+  FD_SET(fileno(stdin), &read_fds);
+
+  tv.tv_sec  = 5;
+  tv.tv_usec = 0;
+  printf("Please press a key in the next %ld seconds\n", (long) tv.tv_sec);
+
+  ret = select(fileno(stdin) + 1, &read_fds, NULL, NULL, &tv);
+  if (ret <= 0)
+    {
+      if (ret == 0)
+	puts("No key pressed");
+      else
+	perror(argv[0]);
+
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  if (!FD_ISSET(fileno(stdin), &read_fds))
+    {
+      puts("stdin should be ready for reading!");
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  /* Try select on stdout. */
+  FD_ZERO(&write_fds);
+
+  FD_SET(fileno(stdout), &write_fds);
+
+  ret = select(fileno(stdout) + 1, NULL, &write_fds, NULL, NULL);
+  if (ret != 1)
+    {
+    }
+
+  if (!FD_ISSET(fileno(stdout), &write_fds))
+    {
+      puts("stdout should be ready for writing!");
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  /* Try select on stderr. */
+  FD_ZERO(&write_fds);
+
+  FD_SET(fileno(stderr), &write_fds);
+
+  ret = select(fileno(stderr) + 1, NULL, &write_fds, NULL, NULL);
+  if (ret != 1)
+    {
+    }
+
+  if (!FD_ISSET(fileno(stderr), &write_fds))
+    {
+      puts("stderr should be ready for writing!");
+      puts("FAIL");
+      return(EXIT_FAILURE);
+    }
+
+  puts("PASS");
+  return(EXIT_SUCCESS);
+}

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019