delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2002/02/02/05:51:33

X-Authentication-Warning: delorie.com: mailnull set sender to djgpp-workers-bounces using -f
Date: Sat, 02 Feb 2002 10:52:47 +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.0.98 (via feedmail 8.3.emacs20_6 I) and Blat ver 1.8.6
Subject: /dev/zero patch
Message-Id: <E16Wxkr-0000Rg-00@phekda.freeserve.co.uk>
Reply-To: djgpp-workers AT delorie DOT com

Hello.

Please find below a revised version of my patch to add /dev/zero
and /dev/full support to DJGPP. The last patch was sent on 2001-01-24.
Here are the changes since the last patch:

* handle restarts by checking __bss_count;
* include <libc/stubs.h>.

OK to commit?

Thanks, bye, Rich

--- /develop/djgpp/include/sys/xdevices.h	Thu Jan  1 00:00:00 1970
+++ /develop/djgpp.rw/include/sys/xdevices.h	Sat Feb  2 10:40:56 2002
@@ -0,0 +1,29 @@
+/* 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 */
+
+#ifndef __dj_include_sys_xdevices_h_
+#define __dj_include_sys_xdevices_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __dj_ENFORCE_ANSI_FREESTANDING
+
+#ifndef __STRICT_ANSI__
+
+#ifndef _POSIX_SOURCE
+
+int __install_dev_zero (void);
+int __install_dev_full (void);
+
+#endif /* !_POSIX_SOURCE */
+#endif /* !__STRICT_ANSI__ */
+#endif /* !__dj_ENFORCE_ANSI_FREESTANDING */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__dj_include_sys_xdevices_h_ */
--- /develop/djgpp/src/docs/kb/wc204.txi	Thu Jan 31 23:00:12 2002
+++ /develop/djgpp.rw/src/docs/kb/wc204.txi	Sat Feb  2 10:41:30 2002
@@ -714,3 +714,11 @@ the @env{DJGPP} environment variable is 
 The functions @code{fstatvfs} and @code{statvfs} have been added.
 @code{statvfs} does much the same as @code{statfs}, but it is
 POSIX-compliant.
+
+@cindex @code{/dev/zero}
+@findex __install_dev_zero AT r{, install support for @file{/dev/zero}}
+@cindex @code{/dev/full}
+@findex __install_dev_full AT r{, install support for @file{/dev/full}}
+Optional support for the devices @file{/dev/zero} and @file{/dev/full} was
+added.  This is enabled using the @code{__install_dev_zero} and
+@code{__install_dev_zero} functions.
--- /develop/djgpp/src/libc/fsext/fse_zero.c	Thu Jan  1 00:00:00 1970
+++ /develop/djgpp.rw/src/libc/fsext/fse_zero.c	Sat Feb  2 10:39:52 2002
@@ -0,0 +1,678 @@
+/* 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 */
+
+/*
+ * fse_zero.c - An implementation of /dev/zero and /dev/full for DJGPP
+ */
+
+#include <libc/stubs.h>
+#include <libc/bss.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <time.h>
+#include <fcntl.h>
+#include <io.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/fsext.h>
+#include <sys/xdevices.h>
+
+static const char DEV_ZERO_PATH[] = "/dev/zero";
+static const char DEV_FULL_PATH[] = "/dev/full";
+
+typedef struct {
+  int flags;
+  int dev_full:1; /* 1 => /dev/full, 0 => /dev/zero */
+  int dup_count;  /* Support for dup() - reference count. */
+} DEV_DATA;
+
+static int    dev_fsext_installed = 0;
+static int    dev_zero_installed  = 0;
+static int    dev_full_installed  = 0;
+
+/* stat(), fstat() support */
+static time_t dev_zero_atime;
+static time_t dev_zero_ctime;
+static ino_t  dev_zero_inode = 0;
+static time_t dev_zero_mtime;
+
+static time_t dev_full_atime;
+static time_t dev_full_ctime;
+static ino_t  dev_full_inode = 0;
+static time_t dev_full_mtime;
+
+extern ino_t _invent_inode (const char *name,
+			    unsigned time_stamp,
+			    unsigned long fsize);
+
+static int dev_fsext (__FSEXT_Fnumber n, int *rv, va_list args);
+
+/* ----------------
+ * - internal_dup -
+ * ---------------- */
+
+static int
+internal_dup (const int fd)
+{
+  DEV_DATA *data   = NULL;  
+  int       new_fd = 0;
+
+  /* Allocate a new file descriptor. */
+  new_fd = __FSEXT_alloc_fd(dev_fsext);
+  if (new_fd < 0)
+    return(-1);
+
+  /* Get context */
+  data = (DEV_DATA *) __FSEXT_get_data(fd);
+  if (data == NULL) {
+    errno = EBADF;
+    _close(new_fd);
+    return(-1);
+  }
+
+  /* Associate the context with the new fd too. */
+  if (__FSEXT_set_data(new_fd, (void *) data) == NULL) {
+    errno = ENOMEM;
+    _close(new_fd);
+    return(-1);
+  }
+
+  data->dup_count++;
+  return(new_fd);
+}
+
+/* ------------------------
+ * - internal_stat_simple -
+ * ------------------------ */
+
+/* This sets up the "obvious" fields of 'struct stat'. */
+
+static int inline
+internal_stat_simple (struct stat *sbuf)
+{
+  sbuf->st_dev     = -1;
+  sbuf->st_gid     = getgid();
+  /* Everyone can read & write the zero device; it's a character device. */
+  sbuf->st_mode    = S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH|S_IFCHR;
+  sbuf->st_nlink   = 1;
+  sbuf->st_size    = 0;
+  sbuf->st_blksize = 0;
+  sbuf->st_uid     = getuid();
+
+#ifdef HAVE_ST_RDEV
+  sbuf->st_rdev    = -1;
+#endif
+
+  return(1);
+}
+
+/* --------------------------
+ * - dev_zero_internal_stat -
+ * -------------------------- */
+
+static int
+dev_zero_internal_stat (struct stat *sbuf)
+{
+  internal_stat_simple(sbuf);
+
+  sbuf->st_atime = dev_zero_atime;
+  sbuf->st_ctime = dev_zero_ctime;
+  sbuf->st_ino   = dev_zero_inode;
+  sbuf->st_mtime = dev_zero_mtime;
+
+  return(1);
+}
+
+/* --------------------------
+ * - dev_full_internal_stat -
+ * -------------------------- */
+
+/* This sets up the "obvious" fields of 'struct stat' for full device. */
+
+static int
+dev_full_internal_stat (struct stat *sbuf)
+{
+  internal_stat_simple(sbuf);
+
+  sbuf->st_atime = dev_full_atime;
+  sbuf->st_ctime = dev_full_ctime;
+  sbuf->st_ino   = dev_full_inode;
+  sbuf->st_mtime = dev_full_mtime;
+
+  return(1);
+}
+
+/* ------------------
+ * - match_dev_path -
+ * ------------------ */
+
+static int inline
+match_dev_path (const char *filename, const char *dev_path)
+{
+  int ret = 1; /* Successful match by default */
+
+  if (filename[0] && (filename[1] == ':')) {
+    /* Drive-extended */
+    if (stricmp(filename + 2, dev_path) != 0) {
+      ret = 0;
+    }
+  } else {
+    /* Normal */
+    if (stricmp(filename, dev_path) != 0) {
+      ret =0;
+    }
+  }
+
+  return(ret);
+}
+
+/* -------------
+ * - dev_fsext -
+ * ------------- */
+
+static int
+dev_fsext (__FSEXT_Fnumber n, int *rv, va_list args)
+{
+  int          emul       = 0; /* Emulated call? 1 => yes, 0 = no. */
+  int          fd         = 0;
+  DEV_DATA    *data       = NULL;
+  char        *filename   = NULL;
+  int          open_mode  = 0;
+  int          perm       = 0;
+  mode_t       creat_mode = 0;  
+  void        *buf        = NULL;
+  size_t       buflen     = 0;
+  off_t        offset     = 0;
+  int          whence     = 0;
+  struct stat *sbuf       = NULL;
+  int          cmd        = 0;
+  int          iparam     = 0;
+#ifdef DJGPP_SUPPORTS_FIONBIO_NOW
+  int         *piparam    = NULL;
+#endif /* DJGPP_SUPPORTS_FIONBIO_NOW */
+
+  switch(n) {
+  default:
+  case __FSEXT_nop:
+    break;
+
+  case __FSEXT_open:
+    filename  = va_arg(args, char *);
+    open_mode = va_arg(args, int);
+
+    if (open_mode & O_CREAT)
+      perm = va_arg(args, int);
+
+    /* Check the filename */
+    if (   !match_dev_path(filename, DEV_ZERO_PATH)
+	&& !match_dev_path(filename, DEV_FULL_PATH))
+      break;
+    
+    /* It's for us. */
+    emul = 1;
+
+    /* Check whether the zero/full device has been installed. */
+    if (match_dev_path(filename, DEV_ZERO_PATH) && !dev_zero_installed)
+      break;
+
+    if (match_dev_path(filename, DEV_FULL_PATH) && !dev_full_installed)
+      break;
+
+    /* zero device _always_ exists. */
+    if (open_mode & O_CREAT) {
+      errno = EEXIST;
+      *rv   = -1;	
+      break;
+    }
+
+    /* Allocate a file descriptor for the device. */
+    fd = __FSEXT_alloc_fd(dev_fsext);
+    if (fd < 0) {
+      *rv = fd;
+      break;
+    }
+
+    /* Allocate some fd context. */
+    data = (DEV_DATA *) malloc(sizeof(*data));
+    if (data == NULL) {
+      errno = ENOMEM;
+      *rv   = -1;
+      break;
+    }
+    
+    /* Set up the context. */
+    memset(data, 0, sizeof(*data));
+
+    data->flags     = open_mode; /* Save open mode for read(), write() */
+    data->dup_count = 1;
+
+    /* Is this zero or full device? */
+    if (match_dev_path(filename, DEV_FULL_PATH))
+      data->dev_full = 1;
+    else
+      data->dev_full = 0;
+
+    /* zero device always has O_NOINHERIT. */
+    data->flags |= O_NOINHERIT;
+
+    /* Ensure that we only have relevant flags. */
+    data->flags &= (O_ACCMODE | O_NOINHERIT | O_NONBLOCK);
+
+    /* Associate the context with the fd. */
+    if (__FSEXT_set_data(fd, (void *) data) == NULL) {
+      errno = ENOMEM;
+      *rv   = -1;
+      break;
+    }
+
+    /* Done */
+    *rv = fd;
+    break;
+
+  case __FSEXT_creat:
+    filename   = va_arg(args, char *);
+    creat_mode = va_arg(args, mode_t);
+
+    /* Check the filename */
+    if (   !match_dev_path(filename, DEV_ZERO_PATH)
+	&& !match_dev_path(filename, DEV_FULL_PATH))
+      break;
+
+    /* Check whether the zero/full device has been installed. */
+    if (match_dev_path(filename, DEV_ZERO_PATH) && !dev_zero_installed)
+      break;
+
+    if (match_dev_path(filename, DEV_FULL_PATH) && !dev_full_installed)
+      break;
+
+    /* zero device _always_ exists. */
+    emul  = 1;
+    errno = EEXIST;
+    *rv   = -1;
+    break;
+
+  case __FSEXT_read:
+    fd     = va_arg(args, int);
+    buf    = va_arg(args, void *);
+    buflen = va_arg(args, size_t);
+
+    /* This must be emulated, since the FSEXT has been called. */
+    emul = 1;
+
+    /* Get context */
+    data = (DEV_DATA *) __FSEXT_get_data(fd);
+    if (data == NULL) {
+      errno = EBADF;
+      *rv   = -1;
+      break;
+    }
+
+    /* Can we actually read from the zero device? */
+    if (   ((data->flags & O_ACCMODE) != O_RDONLY)
+	&& ((data->flags & O_ACCMODE) != O_RDWR) ) {
+      errno = EACCES;
+      *rv   = -1;
+      break;
+    }
+
+    /* Now read - just zero the buffer. */
+    memset(buf, '\0', buflen);
+
+    /* Update access time */
+    if (data->dev_full)
+      time(&dev_full_atime);
+    else
+      time(&dev_zero_atime);
+
+    *rv = (int) buflen;
+    break;
+
+  case __FSEXT_write:
+    fd     = va_arg(args, int);
+    buf    = va_arg(args, void *);
+    buflen = va_arg(args, size_t);
+
+    /* This must be emulated, since the FSEXT has been called. */
+    emul = 1;
+
+    /* Get context */
+    data = (DEV_DATA *) __FSEXT_get_data(fd);
+    if (data == NULL) {
+      errno = EBADF;
+      *rv   = -1;
+      break;
+    }
+
+    /* Can we actually write to the zero device? */
+    if (   ((data->flags & O_ACCMODE) != O_WRONLY)
+	&& ((data->flags & O_ACCMODE) != O_RDWR) ) {
+      errno = EACCES;
+      *rv   = -1;
+      break;
+    }
+
+    if (data->dev_full) {
+      /* Nope, it's full */
+      errno = ENOSPC;
+      *rv   = -1;
+      break;
+    }
+
+    /* Now write - just ignore the buffer. */
+
+    /* Update access & modification times - it must be zero device here. */
+    time(&dev_zero_atime);
+    time(&dev_zero_mtime);
+    
+    *rv = (int) buflen;    
+    break;
+
+  case __FSEXT_ready:
+    /* This must be emulated, since the FSEXT has been called. */
+    emul = 1;
+
+    *rv  = __FSEXT_ready_read | __FSEXT_ready_write;    
+    break;
+
+  case __FSEXT_close:
+    fd = va_arg(args, int);
+
+    /* This must be emulated, since the FSEXT has been called. */
+    emul = 1;
+
+    /* Get context */
+    data = (DEV_DATA *) __FSEXT_get_data(fd);
+    if (data == NULL) {
+      errno = EBADF;
+      *rv   = -1;
+      break;
+    }
+
+    __FSEXT_set_data(fd, NULL);    
+    __FSEXT_set_function(fd, NULL);
+
+    /* Cope with dup()'d zero devices. */
+    data->dup_count--;
+
+    if (data->dup_count <= 0) {
+      /* No longer referenced */
+      free(data);      
+    }
+
+    _close(fd);
+    break;
+
+  case __FSEXT_fcntl:
+    fd  = va_arg(args, int);
+    cmd = va_arg(args, int);
+
+    /* This must be emulated, since the FSEXT has been called. */
+    emul = 1;
+
+    /* Get context */
+    data = (DEV_DATA *) __FSEXT_get_data(fd);
+    if (data == NULL) {
+      errno = EBADF;
+      *rv   = -1;
+      break;
+    }
+
+    switch(cmd) {
+    case F_DUPFD:
+      *rv = internal_dup(fd);
+      break;
+
+    case F_GETFD:
+      if (data->flags & O_NOINHERIT)
+	*rv = 1;
+      else
+	*rv = 0;
+      break;
+
+    case F_SETFD:
+      iparam = va_arg(args, int);
+
+      /* Unsupported - this can't be changed on DOS/Windows. */
+      errno = ENOSYS;
+      *rv   = -1;
+      break;
+
+    case F_GETFL:
+      *rv = data->flags;
+      break;
+
+    case F_SETFL:
+      iparam = va_arg(args, int);
+
+      /* Trying to change immutable fields? */
+      if ((iparam & ~O_NONBLOCK) != (data->flags & ~O_NONBLOCK)) {
+	errno = ENOSYS;
+	*rv   = -1;
+	break;
+      }
+      
+      /* Handle mutable fields */
+      if (iparam & O_NONBLOCK)
+	data->flags |= O_NONBLOCK;
+      else
+	data->flags &= ~O_NONBLOCK;
+
+      *rv = 0;
+      break;
+
+    default:
+      /* Unknown/unhandled fcntl */
+      errno = ENOSYS;
+      *rv   = -1;
+      break;
+    }
+    break;
+
+  case __FSEXT_ioctl:
+    fd  = va_arg(args, int);
+    cmd = va_arg(args, int);
+
+    /* This must be emulated, since the FSEXT has been called. */
+    emul = 1;    
+
+    switch(cmd) {
+      /* */
+#ifdef DJGPP_SUPPORTS_FIONBIO_NOW
+    case FIONBIO:
+      piparam = va_arg(args, int *);
+      if (*piparam)
+	data->flags |= O_NONBLOCK;
+      else
+	data->flags &= ~O_NONBLOCK;
+
+      *rv = 0;
+      break;
+#endif /* DJGPP_SUPPORTS_FIONBIO_NOW */
+      
+    default:
+      errno = ENOTTY;
+      *rv = 1;
+      break;
+    }    
+    break;
+
+  case __FSEXT_lseek:
+    fd     = va_arg(args, int);
+    offset = va_arg(args, off_t);
+    whence = va_arg(args, int);    
+
+    /* This must be emulated, since the FSEXT has been called. */
+    emul = 1;
+
+    *rv = 0;
+    break;
+
+  case __FSEXT_link:
+    /* This must be emulated, since the FSEXT has been called. */
+    emul = 1;
+
+    /* Fail request */
+    errno = EPERM;
+    *rv   = -1;
+    break;
+
+  case __FSEXT_unlink:    
+    /* This must be emulated, since the FSEXT has been called. */
+    emul = 1;
+
+    /* The zero device cannot be removed. */
+    errno = EPERM;
+    *rv   = -1;
+    break;
+
+  case __FSEXT_dup:
+#ifdef DJGPP_SUPPORTS_FSEXT_DUP_NOW
+    fd = va_arg(args, int);
+
+    /* This must be emulated, since the FSEXT has been called. */
+    emul = 1;
+
+    /* Done */
+    *rv = internal_dup(fd);
+#endif /* DJGPP_SUPPORTS_FSEXT_DUP_NOW */
+    break;
+
+  case __FSEXT_dup2:
+#ifdef DJGPP_SUPPORTS_FSEXT_DUP2_NOW
+    /* TODO: When __FSEXT_dup2 is supported, add support. */
+#endif /* DJGPP_SUPPORTS_FSEXT_DUP2_NOW */
+    break;
+
+  case __FSEXT_stat:
+    filename = va_arg(args, char *);
+    sbuf     = va_arg(args, struct stat *);
+
+    /* Check the filename */
+    if (   !match_dev_path(filename, DEV_ZERO_PATH)
+	&& !match_dev_path(filename, DEV_FULL_PATH))
+      break;
+
+    /* Check whether the zero/full device has been installed. */
+    if (match_dev_path(filename, DEV_ZERO_PATH) && !dev_zero_installed)
+      break;
+
+    if (match_dev_path(filename, DEV_FULL_PATH) && !dev_full_installed)
+      break;
+    
+    /* It's for us. */
+    emul = 1;
+
+    /* Set up the stat buf */
+    memset(sbuf, 0, sizeof(*sbuf));
+
+    if (match_dev_path(filename, DEV_FULL_PATH))
+      dev_full_internal_stat(sbuf);
+    else
+      dev_zero_internal_stat(sbuf);
+
+    *rv  = 0;
+    break;
+
+  case __FSEXT_fstat:
+    fd   = va_arg(args, int);
+    sbuf = va_arg(args, struct stat *);
+
+    /* This must be emulated, since the FSEXT has been called. */
+    emul = 1;
+
+    /* Get context */
+    data = (DEV_DATA *) __FSEXT_get_data(fd);
+    if (data == NULL) {
+      errno = EBADF;
+      *rv   = -1;
+      break;
+    }
+
+    /* Set up the stat buf */
+    memset(sbuf, 0, sizeof(*sbuf));
+
+    if (data->dev_full)
+      dev_full_internal_stat(sbuf);
+    else
+      dev_zero_internal_stat(sbuf);
+
+    *rv = 0;
+    break;
+  }  
+
+  return(emul);
+}
+
+/* -------------
+ * - check_bss -
+ * ------------- */
+
+/* Force initialisation in restarted programs, e.g. Emacs. */
+
+static int dev_zero_bss_count = -1;
+
+static void
+check_bss_count (void)
+{
+  if (dev_zero_bss_count == __bss_count)
+    return;
+
+  /* Save __bss_count. */
+  dev_zero_bss_count = __bss_count;
+
+  /* Force (re-)initialisation. */
+  dev_fsext_installed = dev_zero_installed = dev_full_installed = 0;
+}
+
+/* ----------------------
+ * - __install_dev_zero -
+ * ---------------------- */
+
+int
+__install_dev_zero (void)
+{
+  check_bss_count();
+
+  if (dev_zero_installed)
+    return(dev_zero_installed);
+
+  if (!dev_fsext_installed) {
+    __FSEXT_add_open_handler(dev_fsext);
+    dev_fsext_installed = 1;
+  }
+
+  time(&dev_zero_ctime);
+  dev_zero_atime     = dev_zero_mtime = dev_zero_ctime;
+  dev_zero_inode     = _invent_inode(DEV_ZERO_PATH, 0, 0);
+  dev_zero_installed = 1;
+
+  return(1);
+}
+
+/* ----------------------
+ * - __install_dev_full -
+ * ---------------------- */
+
+int
+__install_dev_full (void)
+{
+  check_bss_count();
+
+  if (dev_full_installed)
+    return(dev_full_installed);
+
+  if (!dev_fsext_installed) {
+    __FSEXT_add_open_handler(dev_fsext);
+    dev_fsext_installed = 1;
+  }
+
+  time(&dev_full_ctime);
+  dev_full_atime     = dev_full_mtime = dev_full_ctime;
+  dev_full_inode     = _invent_inode(DEV_FULL_PATH, 0, 0);
+  dev_full_installed = 1;
+
+  return(1);
+}
--- /develop/djgpp/src/libc/fsext/fse_zero.txh	Thu Jan  1 00:00:00 1970
+++ /develop/djgpp.rw/src/libc/fsext/fse_zero.txh	Sat Feb  2 10:24:32 2002
@@ -0,0 +1,55 @@
+@node __install_dev_zero, file system
+
+@subheading Syntax
+
+@example
+#include <sys/xdevices.h>
+
+int __install_dev_zero (void);
+@end example
+
+@subheading Description
+
+This function activates support for the special file @file{/dev/zero}.
+When read, @file{dev/zero} always returns @samp{\0} characters.  When
+written, @file{/dev/zero} discards the data.  Seeks on @file{/dev/zero}
+will always succeed.
+
+The DJGPP debug support functions will interfere with @file{/dev/zero}
+(@pxref{File System Extensions}).
+
+@subheading Return Value
+
+On success, a non-zero value is returned; on failure, zero is returned.
+
+@subheading Portability
+
+@portability !ansi, !posix
+
+@node __install_dev_full, file system
+
+@subheading Syntax
+
+@example
+#include <sys/xdevices.h>
+
+int __install_dev_full (void);
+@end example
+
+@subheading Description
+
+This function activates support for the special file @file{/dev/full}.
+When read, @file{dev/full} always returns @samp{\0} characters.  Writes
+to @file{/dev/full} will fail with @code{errno} set to @code{ENOSPC}.
+Seeks on @file{/dev/full} always succeed.
+
+The DJGPP debug support functions will interfere with @file{/dev/full}
+(@pxref{File System Extensions}).
+
+@subheading Return Value
+
+On success, a non-zero value is returned; on failure, zero is returned.
+
+@subheading Portability
+
+@portability !ansi, !posix
--- /develop/djgpp/src/libc/fsext/makefile	Sat Nov 25 20:38:58 1995
+++ /develop/djgpp.rw/src/libc/fsext/makefile	Sat Feb  2 10:40:24 2002
@@ -1,7 +1,11 @@
+# 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
 # Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details
 TOP=..
 
 SRC += fse_open.c
 SRC += fsext.c
+SRC += fse_zero.c
 
 include $(TOP)/../makefile.inc
--- /develop/djgpp/tests/libc/fsext/tzero.c	Thu Jan  1 00:00:00 1970
+++ /develop/djgpp.rw/tests/libc/fsext/tzero.c	Sat Feb  2 10:40:10 2002
@@ -0,0 +1,537 @@
+/* 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 */
+
+/* Test written by Richard Dawe <richdawe AT bigfoot DOT com> */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <assert.h>
+
+#include <sys/xdevices.h>
+
+static const char DEV_ZERO_PATH[] = "/dev/zero";
+static const char DEV_FULL_PATH[] = "/dev/full";
+
+static int
+jumble_buffer (char *buf, size_t buflen)
+{
+  size_t i;
+
+  for (i = 0; i < buflen; i++) {
+    buf[i] = random() % 0xff;
+  }
+
+  return(1);
+}
+
+static int
+dump_stat (struct stat *sbuf)
+{
+  printf("st_atime   = %d\n"
+	 "st_ctime   = %d\n"
+	 "st_dev     = %d\n"
+	 "st_gid     = %d\n"
+	 "st_ino     = %d\n"
+	 "st_mode    = %d\n"
+	 "st_mtime   = %d\n"
+	 "st_nlink   = %d\n"
+	 "st_size    = %d\n"
+	 "st_blksize = %d\n"
+	 "st_uid     = %d\n",
+	 sbuf->st_atime, sbuf->st_ctime, sbuf->st_dev,
+	 sbuf->st_gid, sbuf->st_ino, sbuf->st_mode,
+	 sbuf->st_mtime, sbuf->st_nlink, sbuf->st_size,
+	 sbuf->st_blksize, sbuf->st_uid);
+
+  return(1);
+}
+
+static int
+test_fstat (const char *filename)
+{
+  struct stat sbuf;
+  char        buf[32768];
+  int         fd = 0;
+  int         n  = 0;
+
+  fd = open(filename, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s: %s\n", filename, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  sleep(1);
+
+  n = read(fd, buf, sizeof(buf));
+  if (n < 0) {
+    fprintf(stderr,
+	    "Unable to read %lu bytes from %s: %s\n",
+	    sizeof(buf), filename, strerror(errno));
+  }
+
+  sleep(1);
+
+  n = write(fd, buf, sizeof(buf));
+  if (n < 0) {
+    fprintf(stderr,
+	    "Unable to write %lu bytes to %s: %s\n",
+	    sizeof(buf), filename, strerror(errno));
+  }
+
+  if (fstat(fd, &sbuf) < 0) {
+    fprintf(stderr,
+	    "Unable to fstat() %s: %s\n", filename, strerror(errno));
+    close(fd);
+    return(0);
+  }
+
+  printf("fstat() result for %s:\n", filename);
+  dump_stat(&sbuf);
+  printf("\n");
+
+  close(fd);
+
+  return(1);
+}
+
+int
+main (int argc, char *argv[])
+{
+  char           buf[32768];
+  char           filename[PATH_MAX];
+  int            fd         = 0;
+  int            new_fd     = 0;
+  fd_set         readfds, writefds;
+  struct timeval tv;
+  struct stat    sbuf;
+  off_t          offset     = 0;
+  off_t          ret_offset = 0;
+  int            n          = 0;
+  size_t         i          = 0;
+
+  if (!__install_dev_zero()) {
+    fprintf(stderr, "__install_dev_zero() failed\n");
+    return(EXIT_FAILURE);
+  }
+
+  if (!__install_dev_full()) {
+    fprintf(stderr, "__install_dev_full() failed\n");
+    return(EXIT_FAILURE);
+  }
+
+  /* - Test open() - */
+
+  /* Normal path */
+  fd = open(DEV_ZERO_PATH, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  close(fd);
+
+  fd = open(DEV_FULL_PATH, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s: %s\n", DEV_FULL_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  close(fd);
+
+  /* Drive-extended path */
+  sprintf(filename, "c:%s", DEV_ZERO_PATH);
+
+  fd = open(filename, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s: %s\n", filename, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  close(fd);
+
+  /* Upper case */
+  strcpy(filename, DEV_ZERO_PATH);
+  strupr(filename);
+
+  fd = open(filename, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s: %s\n", filename, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  close(fd);
+
+  /* - Generic tests of /dev/zero. - */
+  fd = open(DEV_ZERO_PATH, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  /* Write buffer into /dev/zero. */
+  jumble_buffer(buf, sizeof(buf));
+
+  n = write(fd, buf, sizeof(buf));
+  if (n < 0) {
+    fprintf(stderr,
+	    "Unable to write %lu bytes to %s: %s\n",
+	    sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  assert(((size_t) n) == sizeof(buf));
+
+  /* Zero buffer by reading from /dev/zero. */
+  n = read(fd, buf, sizeof(buf));
+  if (n < 0) {
+    fprintf(stderr,
+	    "Unable to read %lu bytes from %s: %s\n",
+	    sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  assert(((size_t) n) == sizeof(buf));
+
+  for (i = 0; i < sizeof(buf); i++) {
+    if (buf[i] != '\0') {
+      fprintf(stderr, "Byte %lu in read data is non-zero\n", i);
+      return(EXIT_FAILURE);
+    }
+  }
+
+  close(fd);
+
+  /* - Generic tests for /dev/full - */
+
+  /* Writing to /dev/full is tested by test_fstat() later on. */
+
+  /* - Test /dev/zero opened read-only. - */
+  fd = open(DEV_ZERO_PATH, O_RDONLY);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s read-only: %s\n",
+	    DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  /* Check that writing fails. */
+  jumble_buffer(buf, sizeof(buf));
+
+  n = write(fd, buf, sizeof(buf));
+  if (n >= 0) {
+    fprintf(stderr,
+	    "Able to write %lu bytes to %s when read-only: %s\n",
+	    sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  /* Zero buffer by reading from /dev/zero. */
+  n = read(fd, buf, sizeof(buf));
+
+  if (n < 0) {
+    fprintf(stderr,
+	    "Unable to read %lu bytes from %s when read-only: %s\n",
+	    sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  assert(((size_t) n) == sizeof(buf));
+
+  close(fd);
+
+  /* - Test /dev/zero opened write-only. - */
+  fd = open(DEV_ZERO_PATH, O_WRONLY);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s write-only: %s\n",
+	    DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  /* Write buffer into /dev/zero. */
+  jumble_buffer(buf, sizeof(buf));
+
+  n = write(fd, buf, sizeof(buf));
+  if (n < 0) {
+    fprintf(stderr,
+	    "Unable to write %lu bytes to %s when write-only: %s\n",
+	    sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  assert(((size_t) n) == sizeof(buf));
+
+  /* Check that reading fails. */  
+  n = read(fd, buf, sizeof(buf));
+  if (n >= 0) {
+    fprintf(stderr,
+	    "Able to read %lu bytes from %s when write-only: %s\n",
+	    sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  close(fd);
+
+  /* - Check that creat() fails. - */
+  fd = creat(DEV_ZERO_PATH, S_IRUSR|S_IWUSR);
+  if (fd >= 0) {
+    fprintf(stderr,
+	    "creat() succeeded in creating %s - it should fail\n",
+	    DEV_ZERO_PATH);
+    return(EXIT_FAILURE);
+  }
+
+  assert(errno == EEXIST);
+
+  /* - Check that open() fails, when using O_CREAT. - */
+  fd = open(DEV_ZERO_PATH, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
+  if (fd >= 0) {
+    fprintf(stderr,
+	    "open() succeeded in creating %s - it should fail\n",
+	    DEV_ZERO_PATH);
+    return(EXIT_FAILURE);
+  }
+
+  assert(errno == EEXIST);
+
+  /* - Check select() support. - */
+  fd = open(DEV_ZERO_PATH, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  FD_ZERO(&readfds);
+  FD_ZERO(&writefds);
+  memset(&tv, 0, sizeof(tv));
+
+  FD_SET(fd, &readfds);
+  FD_SET(fd, &writefds);
+
+  n = select(fd + 1, &readfds, &writefds, NULL, &tv);
+  if (n < 0) {
+    fprintf(stderr,
+	    "select() on %sfailed: %s\n", DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  if (!FD_ISSET(fd, &readfds)) {
+    fprintf(stderr, "Expected %s to be ready for reading\n", DEV_ZERO_PATH);
+    return(EXIT_FAILURE);
+  }
+
+  if (!FD_ISSET(fd, &writefds)) {
+    fprintf(stderr, "Expected %s to be ready for writing\n", DEV_ZERO_PATH);
+    return(EXIT_FAILURE);
+  }
+
+  close(fd);
+
+  /* - Check link() fails - */
+#define LINK_TEST_PATH "/temp/wibble"
+
+  n = link(DEV_ZERO_PATH, LINK_TEST_PATH);
+  if (n == 0) {
+    fprintf(stderr,
+	    "link(\"%s\", \"%s\") succeeded - it should fail\n",
+	    DEV_ZERO_PATH, LINK_TEST_PATH);
+    return(EXIT_FAILURE);
+  }
+
+  assert(errno == EPERM);
+
+#undef LINK_TEST_PATH
+
+  /* - Check unlink() fails - */
+  n = unlink(DEV_ZERO_PATH);
+  if (n >= 0) {
+    fprintf(stderr,
+	    "unlink() succeeded in removing %s - it should fail\n",
+	    DEV_ZERO_PATH);
+    return(EXIT_FAILURE);
+  }
+
+  assert(errno == EPERM);
+
+  /* - Check lseek() - */
+  fd = open(DEV_ZERO_PATH, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  for (i = 0; i < 1000; i++) {
+    offset     = (off_t) random();
+    ret_offset = lseek(fd, offset, SEEK_SET);
+
+    if (ret_offset < 0)
+      fprintf(stderr, "lseek() to position %d failed\n", offset);
+  }
+
+  close(fd);
+
+  /* - Check dup works - */
+#ifdef DJGPP_SUPPORTS_FSEXT_DUP_NOW
+  fd = open(DEV_ZERO_PATH, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  new_fd = dup(fd);
+  if (new_fd < 0) {
+    fprintf(stderr,
+	    "Unable do dup file descriptor for %s: %s\n",
+	    DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  close(fd);
+
+  /* Zero buffer by reading from /dev/zero. */
+  n = read(new_fd, buf, sizeof(buf));
+  if (n < 0) {
+    fprintf(stderr,
+	    "Unable to read %lu bytes from %s: %s\n",
+	    sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  assert(((size_t) n) == sizeof(buf));
+
+  close(new_fd);
+#endif /* DJGPP_SUPPORTS_FSEXT_DUP_NOW */
+
+  /* - Check fcntl() - */
+  fd = open(DEV_ZERO_PATH, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  new_fd = fcntl(fd, F_DUPFD);
+  if (new_fd < 0) {
+    fprintf(stderr, "F_DUPFD fcntl failed: %s\n", strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  /* Zero buffer by reading from /dev/zero. */
+  n = read(new_fd, buf, sizeof(buf));
+  if (n < 0) {
+    fprintf(stderr,
+	    "Unable to read %lu bytes from %s: %s\n",
+	    sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  assert(((size_t) n) == sizeof(buf));
+
+  /* Close old fd and check we can still read using new_fd. */
+  close(fd);
+
+  n = read(new_fd, buf, sizeof(buf));
+  if (n < 0) {
+    fprintf(stderr,
+	    "Unable to read %lu bytes from %s: %s\n",
+	    sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  assert(((size_t) n) == sizeof(buf));
+
+  close(new_fd);
+
+  /* Now try other fcntls */
+  fd = open(DEV_ZERO_PATH, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr,
+	    "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  n = fcntl(fd, F_GETFD);
+  if (n < 0) {
+    fprintf(stderr, "F_GETFD fcntl failed: %s\n", strerror(errno));
+    return(EXIT_FAILURE);
+  }
+  printf("close-on-exec is %s\n", n ? "enabled" : "disabled");
+
+  n = fcntl(fd, F_SETFD, 1);
+  if (n != -1) {
+    fprintf(stderr, "F_SETFD succeeded - it should fail\n");
+    return(EXIT_FAILURE);
+  }
+
+  assert(errno == ENOSYS);
+
+  n = fcntl(fd, F_GETFL);
+  printf("F_GETFL returns: 0x%x\n", n);
+
+  if (n & O_NONBLOCK) {
+    n = fcntl(fd, F_SETFL, n & ~O_NONBLOCK);
+  } else {
+    n = fcntl(fd, F_SETFL, n | O_NONBLOCK);
+  }
+ 
+  if (n < 0) {
+    fprintf(stderr, "Failed to flip O_NONBLOCK using F_SETFL fcntl: %s\n",
+	    strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  /* Try a bogus fcntl */
+  n = fcntl(fd, 0xff);
+  if (n != -1) {
+    fprintf(stderr, "Bogus fcntl succeeded - it should fail\n");
+    return(EXIT_FAILURE);
+  }
+
+  assert(errno == ENOSYS);
+
+  close(fd);
+
+  /* - Check stat() works - */
+  if (stat(DEV_ZERO_PATH, &sbuf) < 0) {
+    fprintf(stderr,
+	    "Unable to stat() %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  printf("stat() result for %s:\n", DEV_ZERO_PATH);
+  dump_stat(&sbuf);
+  printf("\n");
+
+  if (stat(DEV_FULL_PATH, &sbuf) < 0) {
+    fprintf(stderr,
+	    "Unable to stat() %s: %s\n", DEV_FULL_PATH, strerror(errno));
+    return(EXIT_FAILURE);
+  }
+
+  printf("stat() result for %s:\n", DEV_FULL_PATH);
+  dump_stat(&sbuf);
+  printf("\n");
+
+  /* - Check fstat() works - */
+  test_fstat(DEV_ZERO_PATH);
+  test_fstat(DEV_FULL_PATH);
+
+  return(EXIT_SUCCESS);
+}
--- /develop/djgpp/tests/libc/fsext/makefile	Thu Jan  1 21:33:42 1998
+++ /develop/djgpp.rw/tests/libc/fsext/makefile	Sat Feb  2 10:40:46 2002
@@ -1,6 +1,11 @@
+# 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
+
 TOP=..
 
 SRC += open2.c
 SRC += pipe.c
+SRC += tzero.c
 
 include $(TOP)/../makefile.inc

- Raw text -


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