Date: Wed, 19 Mar 2003 12:46:02 +0000 From: "Richard Dawe" 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: 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 #include #include +#include #include #include + /* 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 . */ +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 @@ -5,9 +6,11 @@ #include #include #include +#include #include #include +#include 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 #include #include +#include +#include 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 #include #include +#include +#include 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, ®s); @@ -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, ®s); - 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 #include #include #include #include +#include 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 @@ -8,6 +9,7 @@ #include #include #include +#include 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 @@ -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 #include #include +#include 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 @@ -6,12 +7,20 @@ #include #include #include +#include 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 #include #include +#include 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 #include #include #include +#include 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 #include @@ -6,30 +7,36 @@ #include #include #include +#include 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 +#include +#include +#include +#include +#include + +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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include + +#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); +}