www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/1997/09/23/16:03:00

Message-Id: <3.0.1.32.19970923150129.006adf94@yacker.xiotech.com>
Date: Tue, 23 Sep 1997 15:01:29 -0500
To: djgpp-workers AT delorie DOT com
From: Randy Maas <randym AT xiotech DOT com>
Subject: Rev c of the proposed fsext changes (part 5)
Mime-Version: 1.0

--=====================_875062889==_
Content-Type: text/plain; charset="us-ascii"

Attached are the sources for the "new" files.  Mostly _DOS*.c and FS_EMU.c
is merged from the libc v2.01 routines.  The _DOS*.c provides a single
replaceable fsext for porting to non-DOS environments.  FS_EMU.c  provides
functionality for those FSEXT that don't emulate every function and don't
need to (eg, link, copy, etc).  _io_api provides _nop(),   _def_fs.c is
what redirects everything automagically to the DOS fsext.

Heres to hoping I got it right this time.  If I didn't, mail me. 8-)

Randy Maas
randym AT acm DOT org
--=====================_875062889==_
Content-Type: text/plain; charset="us-ascii"
Content-Disposition: attachment; filename="_def_fs.c"

/*
   Default FS extension
   1997, Randall Maas
 */

#include <sys/fsext.h>

/*
   @txh
    @node _def_fsext, file system
     @syntax
      @example
*/
int _def_fsext(__FSEXT_Fnumber Op, int* rv, va_list Args, void* State)
/* @end example */
{
   /*
      @subheading Description
       This is the lowest level file system extension.  By default,
       @code{_def_fsext} calls MSDOS (@pxref{_DOS_FS_Entry}).  By providing
       your own @code{_def_fsext} subroutine, the basic file system services
       can be changed.  For instance, if you were trying to implement a win32
       based system (and didn't want MSDOS in the way), you would want to
       provide a dummy @code{_def_fsext} routine that calls your win32
       interface.
      @end txh
    */

   return _DOS_FS_Entry(Op, rv, Args, State);
}

--=====================_875062889==_
Content-Type: text/plain; charset="us-ascii"
Content-Disposition: attachment; filename="_DOS_fd.c"

/*
    1997, Randall Maas -- maked _DOS_alloc_fd separate from __FSEXT_alloc_fd
        so that it can be overridden.
    Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details
*/
#include <dpmi.h>
#include <go32.h>
#include <libc/dosio.h>
#include <errno.h>

/*
   @txh
    @node _DOS_alloc_fd, dos
    @subheading Syntax
     @example
*/
int _DOS_alloc_fd()
/*@end example*/
{
   /*
      @subheading Description
       This function opens DOS's @samp{NUL} device, so as to allocate a
       handle that DOS won't then reuse.  It also assigns the handler
       function for that descriptor.

      @subheading Return Value
       Returns the handle, or -1 on error

      @end txh
    */

  __dpmi_regs r;

  _put_path("nul");
  r.x.ax = 0x3d82;	/* open, no inherit, read/write */
  r.x.dx = __tb_offset;
  r.x.ds = __tb_segment;
  __dpmi_int(0x21, &r);

  if (r.x.flags & 1)
  {
    errno = __doserr_to_errno(r.x.ax);
    return -1;
  }

  return r.x.ax;
}


--=====================_875062889==_
Content-Type: text/plain; charset="us-ascii"
Content-Disposition: attachment; filename="_DOS_fsext.c"

/*
   DOS File System Extension for DJGPP.
   Copyright 1997 Randall Maas.
   1997, Randall Maas remove DOS specific code and inlined documentation.
   Brought in DOS specific calls -- the code for these were written by
   DJ Delorie and by Morten Welinder, these 
   Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details
   Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details

   Copyright 1995 by Morten Welinder
   This file maybe freely distributed and modified as long as the
   copyright notice remains.

   1997, Randall Maas: Split _close into _close and _DOS_close;
        Renamed to _DOS_remove.
        inlined the documentation.
*/

#include <stdio.h>
#include <libc/stubs.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <go32.h>
#include <dpmi.h>
#include <libc/dosio.h>
#include <fsext.h>
#include <unistd.h>
#include <io.h>

static int _DOS_open   (va_list);
static int _DOS_create (va_list);
static int _DOS_close  (va_list);
static int _DOS_read   (va_list);
static int _DOS_write  (va_list);
static int _DOS_ready  (va_list);
static int _DOS_seek   (va_list);
static int _DOS_remove (va_list);

/*
   @txh
    @node _DOS_FS_Entry, dos
    @subheading Syntax
     @example
 */
int _DOS_FS_Entry(Op, RV, Args, State)
        __FSEXT_Fnumber Op;    /*// Operation to perform. */
        int*             RV;   /*// Return value of the operation. */
        va_list          Args; /*// Arguments to the operation. */
        void*            State; /*// Ignored */
/* @end example */
{
   /*
      @subheading Description
       This provides a common entry point for DOS file system functions.
       (see @ref{File System Extensions}).
      @end txh
    */

   if (Op ==  __FSEXT_nop)
     {
        /* No operation. */
        *RV = 0;
        return 1;
     }

   switch(Op)
    {
       /* Check the status DOS based file. */
       case __FSEXT_ready: *RV = _DOS_ready(Args); break;

       /* Read from the DOS based file. */
       case __FSEXT_read: *RV = _DOS_read(Args); break;

       /* Write to the DOS based file. */
       case __FSEXT_write: *RV = _DOS_write(Args); break;

       /* Do a seek with the DOS file */
       case __FSEXT_lseek: *RV = _DOS_seek(Args); break;

       /* Open a DOS based file */
       case __FSEXT_open: *RV = _DOS_open(Args); break;

       /* Create a DOS based file */
       case __FSEXT_creat: *RV = _DOS_create(Args); break;

       /* Close the DOS file. */
       case __FSEXT_close: *RV = _DOS_close(Args); break;

       /* Remove the DOS file. */
       case __FSEXT_unlink: *RV =_DOS_remove(Args); break;

       default: return 0; /* Emulation was not done. */
    }
   return 1;
}

/*
   @txh
    The FS_Ext functions are implemented as follows:
    @table @code
   @end txh
*/

static int _DOS_open(va_list Args)
{
   /*
      @txh
      @item __FSEXT_open
       This is a direct connection to the MS-DOS open function call, int
       0x21, %ah = 0x3d.  The file is set to binary mode.
      @end txh
    */

  __dpmi_regs r;
  int use_lfn = _USE_LFN;
  const char* filename;
  int oflag;

  filename = va_arg(Args, const char*);
  if (filename == 0)
  {
    errno = EINVAL;
    return -1;
    /* Bug: should simply indicate not handled. */
  }

  oflag = va_arg(Args, int);

  _put_path(filename);
  if(use_lfn) {
    r.x.ax = 0x716c;
    r.x.bx = oflag & 0xff;
    r.x.dx = 1;			/* Open existing file */
    r.x.si = __tb_offset;
  } else {
    r.h.ah = 0x3d;
    r.h.al = oflag;
    r.x.dx = __tb_offset;
  }
  r.x.cx = 0;
  r.x.ds = __tb_segment;
  __dpmi_int(0x21, &r);
  if(r.x.flags & 1)
  {
    errno = __doserr_to_errno(r.x.ax);
    return -1;
  }
  __file_handle_set(r.x.ax, O_BINARY);
  return r.x.ax;
}

static int _DOS_create (va_list Args)
{
   /*
     @txh
      @item __FSEXT_creat
       This is a direct connection to the MS-DOS creat function call, int
       0x21, %ah = 0x3c.  The file is set to binary mode.
     @end txh
    */

  __dpmi_regs r;
  unsigned use_lfn = _USE_LFN;
  const char* filename;
  int attrib;

  filename = va_arg(Args, const char*);
  if (filename == 0)
  {
    errno = EINVAL;
    return -1;
    /* BUG: We don't handle this... */
  }

  attrib = va_arg(Args, int);

  _put_path(filename);
  if(use_lfn) {
    r.x.ax = 0x716c;
    r.x.bx = 0x0002;		/* open r/w */
    r.x.dx = 0x0012;		/* Create, truncate if exists */
    r.x.si = __tb_offset;
  } else {
    r.h.ah = 0x3c;
    r.x.dx = __tb_offset;
  }
  r.x.cx = attrib;
  r.x.ds = __tb_segment;
  __dpmi_int(0x21, &r);
  if(r.x.flags & 1)
  {
    errno = __doserr_to_errno(r.x.ax);
    return -1;
  }
  __file_handle_set(r.x.ax, O_BINARY);
  return r.x.ax;
}

static int _DOS_close(va_list Args)
{
   /*
     @txh
      @item __FSEXT_close
       This is a direct connection to the MS-DOS close function call, int
       0x21, %ah = 0x3e.  Returns zero if the file was closed, else nonzero.
      @end txh
    */

  __dpmi_regs r;
  int handle; /* The file descriptor. */

  handle = va_arg(Args, int);

  r.h.ah = 0x3e;
  r.x.bx = handle;
  __dpmi_int(0x21, &r);
  if (r.x.flags & 1)
  {
    errno = EBADF;
    return -1;
  }
  return 0;
}

static int _DOS_seek(va_list Args)
{
   /*
     @txh
      @item __FSEXT_seek
       This is a direct connection to the MS-DOS lseek function call, int
       0x21, %ah = 0x42.  Returns -1 on error, the position within the file
       otherwise.
      @end txh
    */


  __dpmi_regs r;
  int handle, whence;
  off_t offset;

  handle = va_arg(Args, int);
  offset = va_arg(Args, off_t);
  whence = va_arg(Args, int);

  r.h.ah = 0x42;
  r.h.al = whence;
  r.x.bx = handle;
  r.x.cx = offset >> 16;
  r.x.dx = offset & 0xffff;
  __dpmi_int(0x21, &r);
  if (r.x.flags & 1)
  {
    errno = __doserr_to_errno(r.x.ax);
    return -1;
  }
  return (r.x.dx << 16) + r.x.ax;
}

static int _DOS_write(va_list Args)
{
   /*
     @txh 
      @item __FSEXT_write
       This is a direct connection to the MS-DOS write function call, int
       0x21, %ah = 0x40.  No conversion is done on the data; it is written as
       raw binary data.
     @end txh
    */

  size_t j, i;
  int nput;
  unsigned long tbsize;
  __dpmi_regs r;
   int handle;
   const void* buffer;
   size_t count;

   handle = va_arg(Args, int);
   buffer = va_arg(Args, void*);
   count  = va_arg(Args, size_t);

  tbsize = _go32_info_block.size_of_transfer_buffer;
  nput = 0;
  do {
    j = (count <= tbsize) ? count : tbsize;
    if (j)
      dosmemput(buffer, j, __tb);
    r.x.ax = 0x4000;
    r.x.bx = handle;
    r.x.cx = j;
    r.x.dx = __tb & 15;
    r.x.ds = __tb / 16;
    __dpmi_int(0x21, &r);
    if (r.x.flags & 1)
    {
      errno = __doserr_to_errno(r.x.ax);
      return -1;
    }
    i = r.x.ax;
    count -= i;
    buffer = (void *)((int)buffer + i);
    nput += i;
  } while(count && (i == j));

  if (count && nput == 0)
  {
    errno = ENOSPC;
    return -1;
  }

  return nput;
}

static int _DOS_read(va_list Args)
{
   /*
      @txh
      @item __FSEXT_read
       This is a direct connection to the MS-DOS read function call, int
       0x21, %ah = 0x3f.  No conversion is done on the data; it is read as
       raw binary data.
      @end txh
    */

  size_t j, k;
  int ngot;
  unsigned long tbsize;
  __dpmi_regs r;
   int handle;
   void* buffer;
   size_t count;

   handle = va_arg(Args, int);
   buffer = va_arg(Args, void*);
   count  = va_arg(Args, size_t);

  tbsize = _go32_info_block.size_of_transfer_buffer;
  ngot = 0;
  do {
    j = (count <= tbsize) ? count : tbsize;
    r.x.ax = 0x3f00;
    r.x.bx = handle;
    r.x.cx = j;
    r.x.dx = __tb & 15;
    r.x.ds = __tb / 16;
    __dpmi_int(0x21, &r);
    if(r.x.flags & 1)
    {
      errno = __doserr_to_errno(r.x.ax);
      return -1;
    }
    count -= j;
    k = r.x.ax;
    ngot += k;
    if (k)
      dosmemget(__tb, k, buffer);
    buffer = (void *)((int)buffer + k);
  } while(count && j == k);	/* if not == on DOS then no more */
  return ngot;
}

/*
   Most of this is credited to Morten Welinder

   This is as close as we get, I think.  For a file connected to a printer
   we could of course go ask the BIOS, but this should be enough.  */


/* The Dos call 4407 always returns TRUE for disk files.  So the
   following really is meaningful for character devices only...  */

inline static int _DOS_output_ready(int fd)
{

  __dpmi_regs regs;

  regs.x.ax = 0x4407;
  regs.x.bx = fd;
  __dpmi_int (0x21, &regs);
  if (regs.x.flags & 1)
  {
    errno = __doserr_to_errno (regs.x.ax);
    return -1;
  }
  else
    return regs.h.al == 0xff;
}

inline static int _DOS_input_ready(int fd)
{

  __dpmi_regs regs;

  regs.x.ax = 0x4406;
  regs.x.bx = fd;
  __dpmi_int (0x21, &regs);
  if (regs.x.flags & 1)
  {
    errno = __doserr_to_errno (regs.x.ax);
    return -1;
  }
  else
    return regs.h.al == 0xff;
}

static int _DOS_ready(va_list Args)
{
   int Ret = 0, X, fd;

   fd = va_arg(Args, int);

   X = _DOS_input_ready(fd);
   Ret |= (X < 0) ?  __FSEXT_ready_error : (X) ? __FSEXT_ready_read: 0;

   X = _DOS_output_ready(fd);
   Ret |= (X < 0) ?  __FSEXT_ready_error : (X) ? __FSEXT_ready_write: 0;

   return Ret;
}

static int _DOS_remove(va_list Args)
{
  __dpmi_regs r;
  unsigned attr;
  int directory_p;
  int use_lfn = _USE_LFN;
  const char *fn = va_arg(Args, const char*);
 
  /* Get the file attribute byte.  */
  attr = _chmod(fn, 0);
  directory_p = attr & 0x10;
 
  /* Now, make the file writable.  We must reset Vol, Dir, Sys and Hidden bits 
     in addition to the Read-Only bit, or else 214301 will fail.  */
  _chmod(fn, 1, attr & 0xffe0);

  /* Now delete it.  Note, _chmod leaves dir name in tranfer buffer. */
  if (directory_p)
    r.h.ah = 0x3a;		/* DOS Remove Directory function */
  else
    r.h.ah = 0x41;		/* DOS Remove File function */
  if(use_lfn) {
    r.h.al = r.h.ah;
    r.h.ah = 0x71;
    r.x.si = 0;			/* No Wildcards */
  }
  r.x.dx = __tb_offset;
  r.x.ds = __tb_segment;
  __dpmi_int(0x21, &r);
  if(r.x.flags & 1)
  {
    /* We failed.  Leave the things as we've found them.  */
    int e = __doserr_to_errno(r.x.ax);
 
    _chmod(fn, 1, attr & 0xffe7);
    errno = e;
    return -1;
  }
  return 0;
}

/*
   @txh
    @end table
   @end txh
 */

--=====================_875062889==_
Content-Type: text/plain; charset="us-ascii"
Content-Disposition: attachment; filename="_fs_call.c"

/*
   File System Call.
   1997 Randall Maas.
   Portions Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details
*/
#include <stdio.h>
/* The test is her for using this code on a couple of non DJGPP platforms as well */
#if defined(__djgpp__)
#  include <sys/fsext.h>
# else
#  include "fsext.h"
#endif
#include <errno.h>

/*
   @txh
    @node __FSEXT_Call, file system
    @subheading Syntax
     @example
 */
int __FSEXT_Call(__FSEXT_Fnumber Op, int Handle, va_list Args)
        /*//Op     identifies which function is to be emulated.*/
        /*//Handle is the handle to entity to worked on.*/
        /*//Args   represents the arguments passed to the original function */
/*@end example */
{
   /*
      @subheading Description
       This procedure is meant to be called by functions require some form of
       emulation.  It calls the File System Extension associated with the
       Handle.  If the function is not emulated by this extension, it tries
       a generic function emulator to carry out the operation.

      @subheading Return Value
       If it is unable to find a handler that corresponds to the handle, or
       the handler did not carry out the operation, @code{_FS_Call} sets
       @var{errno} to EINVAL and returns -1.  Otherwise it returns the
       value from the handler.
      @end txh
    */

  __FSEXT_Function *func;
  void* state;
  int rv;

  if (__FSEXT_get_handler(Handle, &func, &state) && func)
    {
      /* Try to call the function. */
      if (func(Op, &rv, Args, state))
        return rv; /* The operation was carried out. */
       else
        {
           /* The operation was not carried out.  Let's see if we can emulate
              the operation in some other way.  We only do this if the
              original File Handler does not implement operation.
            */
           if (__FSEXT_Emu(Op, &rv, Args, NULL))
            return rv;
        }
    }
   else
    {
       /* The handle is not one defined in our FS extensions -- it may be
          one not open internally (stdin, stdout, stderr, or one passed via
          argument).  We punt by letting the default extensions do it  */
       if (_def_fsext(Op, &rv, Args, NULL)) return rv;
    }

  errno = EINVAL;
  return -1;
}

--=====================_875062889==_
Content-Type: text/plain; charset="us-ascii"
Content-Disposition: attachment; filename="_io_api.c"

/*
  Forms the C Library basic IO procedures API.

  1997, Randall Maas rewrote this calls to use __FSEXT_Call
  */
#include <fsext.h>

/*
   @txh
    @node _nop, file system
    @subheading Syntax
     @example
*/
#include <io.h>
int _nop(int handle)
/*@end example*/
{
   /*
      @subheading Description
       This performs no operation on the IO device or file (referred to by
       handle).  It's primary purpose is to allow the handler time to mantain
       internals of this entity.  Usually this is connection maintenance in
       the networking world.

      @subheading Return Value
       0 if everything is okay, or -1 on error.
      @end txh
    */

   return __FSEXT_Call(__FSEXT_nop, handle, &handle);
}


--=====================_875062889==_
Content-Type: text/plain; charset="us-ascii"
Content-Disposition: attachment; filename="_lseek.c"

/*
  emulates _lseek

  1997, Randall Maas rewrote this calls to use __FSEXT_Call
  */
#include <sys/fsext.h>
#include <unistd.h>

/*
   @txh
    @node _lseek, file system
    @subheading Syntax
     @example
*/
off_t _lseek(int handle, off_t offset, int whence)
/*@end example*/
{
   /*
      @subheading Description

       This function seeks within the file associated with @var{handle}.
       If the handle is associated with a file system extension, that
       extension will be responsible for emulating the ``lseek''
       functionality.  Otherwise, MSDOS will be called.

      @subheading Return Value
       The number of bytes read, or -1 on error.
      @end txh
    */

   return __FSEXT_Call(__FSEXT_lseek, handle, &handle);
}


--=====================_875062889==_
Content-Type: text/plain; charset="us-ascii"
Content-Disposition: attachment; filename="pread.c"

/* Absolute IO read method.
   Randall Maas
   Emulates the UNIXish pread
   This is useful for "files" on some storage medium.
   BUG: Should set errno
    */
#include <io.h>
#include <sys/fsext.h>

/*
   @txh
    @node pread, io
     @subheading Syntax
      @example
*/
#include <unistd.h>
ssize_t pread (Handle, Buffer, Size, Ofs)
        int   Handle; /*// The IO handle to read from */
        char* Buffer; /*// The buffer to read data into */
        size_t Size;  /*// The size of the buffer */
        off_t Ofs;    /*// The offset within the IO handle to read from */
/* @end example */
{
   /*
      @subheading Description
       This reads from the specified position within the IO channel.  It does
       this in such a way that the current IO position is not affected.

      @subheading Cavaets
       Attempting to use @code{pread} on a IO channel that is incapable of
       seeking will result in an error.
      @subheading Returns
       On success, the number of bytes actually read.
      @end txh
    */

   return __FSEXT_Call(__FSEXT_pread, Handle, &Handle);
}


--=====================_875062889==_
Content-Type: text/plain; charset="us-ascii"
Content-Disposition: attachment; filename="pwrite.c"

/* Absolute IO access methods.
   Randall Maas
   Emulates the UNIXish pread and pwrite
   This is useful for "files" on some storage medium.
   BUG: Should set errno
    */
#include <io.h>
#include <sys/fsext.h>

/*
   @txh
    @node pwrite, io
     @subheading Syntax
      @example
*/
#include <unistd.h>
ssize_t pwrite(Handle, Buffer, Size, Ofs)
        int         Handle; /*// The IO handle to write to */
        const char* Buffer; /*// The buffer to write data into */
        size_t      Size;   /*// The size of the buffer */
        off_t       Ofs;    /*// The offset within the IO handle to write to */
/* @end example */
{
   /*
      @subheading Description
       This writes to the specified position within the IO channel.  It does
       this in such a way that the current IO position is not affected.

      @subheading Cavaets
       Attempting to use @code{pwrite} on a IO channel that is incapable of
       seeking will result in an error.
      @subheading Returns
       On success, the number of bytes actually read.
      @end txh
    */

   return __FSEXT_Call(__FSEXT_pwrite, Handle, &Handle);
}


--=====================_875062889==_
Content-Type: text/plain; charset="us-ascii"
Content-Disposition: attachment; filename="fsemu.c"

/*
    File System emulation for unimplemented functions
    1997- Randall Maas, created __FSEXT_Emu and moved some of the code from libc v2.01 to
          this.  Emulates functions not implemented by some FS extensions.
          Some portability to non-DJGPP environments added.
    Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details
 */

# if defined(__DJGPP__)
#  include <libc/stubs.h>
#  include <utime.h>		/* For utime() */
#  include <unistd.h>		/* For read(), write(), etc. */
#  include <sys/fsext.h>
# else
#  include "fsext.h"
#  include <stdio.h>
#  include <io.h>
#  include <sys/utime.h>
# endif
#include <sys/stat.h>		/* For stat() */
#include <fcntl.h>		/* For O_RDONLY, etc. */
#include <limits.h>		/* For PATH_MAX */
#include <errno.h>		/* For errno */

static int _Emu_create (va_list);
static int _Emu_link (va_list);
static int _Emu_copy (va_list);
static int _Emu_prw (__FSEXT_Fnumber, va_list);

/*
   @txh
   @node __FSEXT_Emu, file system
    @subheading Syntax
     @example
*/
int __FSEXT_Emu(__FSEXT_Fnumber Op, int* rv, va_list Args, void* State)
/* @end example */
{
   /*
      @subheading Description
       This provides some a simple entry point to emulating File System
       Extension functions.  The emulation of these functions is based on
       simpler functions usually provided by the File System Extensions.
      @end txh
    */
   switch(Op)
    {
       case __FSEXT_creat: *rv = _Emu_create (Args); break;
       case __FSEXT_link: *rv = _Emu_link (Args); break;
       case __FSEXT_copy: *rv = _Emu_copy (Args); break;
       case __FSEXT_pread:
       case __FSEXT_pwrite: *rv = _Emu_prw(Op, Args); break;
       default: return 0; /* Not emulated. */
    }
   return 1;
}

/*
   @txh
    Currently the following functions are emulated:
    @table @code
   @end txh
*/

static int _Emu_create(va_list Args)
{
   /*
     @txh
      @item __FSEXT_create
       @code{creat} can be simulate easily by most @code{open} functions --
       just open the file, but with settings to create (@code{O_CREAT}) if
       it does not exist, and truncate (@code{O_TRUNC}) if it does exist.
       (Note: this is a kludge.)
     @end txh
    */
   const char *path1;
   int attrib;

   path1 = va_arg(Args, const char*);
   attrib = va_arg(Args, int);
   return open(path1, O_TRUNC | O_CREAT, attrib);
}

static int _Emu_link(va_list Args)
{
   /*
     @txh
      @item __FSEXT_link
       For those systems that can't really do a link (such as MSDOS), we just
       do a copy instead, which is as close as these systems get.
       Alternatively, we could always fail and return -1.  I think this is
       slightly better.
    */

   const char *path1, *path2;
   path1 = va_arg(Args, const char*);
   path2 = va_arg(Args, const char*);

   /* We call _copy instead of emulating it here in the hopes that a File
      System Extension will properly handle the copying.  If not, it will be
      reflected back, and we will emulate it below in __FSEXT_copy.
      @end txh
    */
   return _copy(path1, path2);
}

static int _Emu_copy(va_list Args)
{
   /*
      @txh
      @item __FSEXT_copy
       This emulates a file copy, using binary read and writes.  Some
       operating systems provide a copy function.  Others, like DOS, do not.
       (Why would an OS want to provide "copy" functionality?  Consider
       Novell's Netware.  To copy a file one one network drive to another,
       this routine will have the data sequentially sent down the network and
       and then send the data right back.  Acceptable for one file, but not
       for a large number of files.  If the OS handled it, it could send a
       command to the Netware server and do the copy right there.  This is
       clearly more efficient, so it is common for network operating
       systems -- and others -- to provide copy functions as part of their
       file system API.)
      @end txh
   */
  struct stat statbuf1, statbuf2;
    struct utimbuf times;
  char buf[16384];
  int fd1, fd2, nbyte, status1, status2;
   const char *path1, *path2;

   path1 = va_arg(Args, const char*);
   path2 = va_arg(Args, const char*);

  /* Fail if either path is null */
  if (path1 == NULL || path2 == NULL)
  {
    errno = EFAULT;
    return -1;
  }
  if (*path1 == '\0' || *path2 == '\0')
  {
    errno = ENOENT;
    return -1;
  }

  /* Fail if path1 does not exist - stat() will set errno */
  if (stat(path1, &statbuf1) < 0) return -1;

# if defined(S_ISREG)
  /* Fail if path1 is not a regular file -- assume it is on platforms without S_ISREG concept*/
  if (!S_ISREG(statbuf1.st_mode))
  {
    errno = EPERM;
    return -1;
  }
# endif

  /* Fail if unable to open path1 - open() will set errno */
  fd1 = open(path1, O_RDONLY | O_BINARY);
  if (fd1 < 0) return -1;

  /* Fail if unable to create path2 - open() will set errno */
  fd2 = open(path2, O_WRONLY | O_BINARY | O_CREAT | O_EXCL, 0600);
  if (fd2 < 0)
  {
    (void) close(fd1);
    return -1;
  }

  /* Fail if path1 and path2 are on different devices */
  if (fstat(fd2, &statbuf2) < 0) return -1;
  if (statbuf1.st_dev != statbuf2.st_dev)
  {
    (void)close(fd1);
    (void)close(fd2);
    (void)unlink(path2);
    errno = EXDEV;
    return -1;
  }

  /* Copy path1 to path2 */
  do
  {
    nbyte = read(fd1, buf, sizeof buf);
    if (nbyte <= 0) break;
    if (write(fd2, buf, nbyte) != nbyte) nbyte = -1;
  }
  while (nbyte > 0);

  /* Fail if the copy failed or we can't clean up */
  status1 = close(fd1);
  status2 = close(fd2);
  if (nbyte < 0 || status1 < 0 || status2 < 0)
  {
    (void) unlink(path2);
    return -1;
  }

  /* Success! */

  /* Set the mode to match the original, ignoring errors */
  (void) chmod(path2, statbuf1.st_mode);

  /* Set the file time to match the original, ignoring errors */
  times.actime = statbuf1.st_atime;
  times.modtime = statbuf1.st_mtime;
  (void) utime(path2, &times);
  return 0;
}

static int _Emu_prw(__FSEXT_Fnumber Op, va_list Args)
{
   /*
      @txh
       @item __FSEXT_pread
        @code{pread} is simulated by @code{lseek}'ing to the appopriate place
        in the file, and read the data.  Upon completion, it @code{lseek}'s
        back to its previous position.  This will fail if the file does not
        support @code{lseek}.

       @item __FSEXT_pwrite
        @code{pread} is simulated by @code{lseek}'ing to the appopriate place
        in the file, and writing the data.  Upon completion, it @code{lseek}'s
        back to its previous position.  This will fail if the file does not
        support @code{lseek}.

      @end
    */

   /* Parse the arguments */
   int    Handle= va_arg(Args, int);
   char*  Buffer= va_arg(Args, char*);
   size_t Size  = va_arg(Args, size_t);
   off_t  Ofs   = va_arg(Args, off_t);
   unsigned long Old = tell(Handle);
   int Ret;

   if (lseek(Handle, Ofs, SEEK_SET) != Ofs) return -1;
   if (Op == __FSEXT_pread)
     Ret = read(Handle, Buffer, Size);
    else
     Ret = write(Handle, Buffer, Size);

   /* Return the IO to its previous position. */
   if (lseek(Handle, Old, SEEK_SET) != Old) return -1;
   return Ret;
}

/*
   @txh
    @end table

    @subheading Notes
     Unfortunately there is no easy way to add functionality to the emulator.

    @subheading See Also
     @pxref{File System Extensions}
*/

--=====================_875062889==_
Content-Type: text/plain; charset="us-ascii"



--=====================_875062889==_--

- Raw text -


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