/* ,file-id archive://[lord]/406/vu/./vu.c/1998-05-18
*/
/*	Copyright (C) 1997 Tom Lord
 * 
 * This program is provided to you under the terms of the Liberty Software
 * License.  You are NOT permitted to redistribute, modify, or use it
 * except in very specific ways described by that license.
 *
 * This software comes with NO WARRANTY.
 * 
 * You should have received a copy of the Liberty Software License
 * along with this software; see the file =LICENSE.  If not, write to
 * the Tom Lord, 1810 Francisco St. #2, Berkeley CA, 94703, USA.  
 */



#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <utime.h>
#include <errno.h>
#include "rx/regex.h"
#include "vu.h"



struct vu_namespace_handler
{
  regex_t * preg;
  int eflags;
  struct vu_fs_discipline * vtable;
};

static int n_fs_handlers = 0;
static struct vu_namespace_handler * fs_handlers = 0;

static int n_fd_handlers = 0;
struct vu_fs_discipline ** fd_handlers = 0;


/* Dynamically setting rules for dispatching on file names and
 * descriptor numbers:
 */

/* A dispatching rule set this way remains in effect
 * forever.
 */
int
vu_push_name_handler (regex_t * preg,
		      int eflags,
		      struct vu_fs_discipline * vtable)
{
  fs_handlers = ((struct vu_namespace_handler *)
		      realloc (fs_handlers,
			       (  (1 + n_fs_handlers)
				* sizeof (struct vu_fs_discipline))));
  if (!fs_handlers)
    return -1;

  memset ((char *)(fs_handlers + n_fs_handlers),
	  0,
	  (   (1 + n_fs_handlers)
	   * sizeof (struct vu_fs_discipline *)));
  fs_handlers[n_fs_handlers].preg = preg;
  fs_handlers[n_fs_handlers].eflags = eflags;
  fs_handlers[n_fs_handlers].vtable = vtable;
  ++n_fs_handlers;
  return 0;
}

/* A dispatching rule set this way remains in effect
 * until the descriptor is closed.
 */
void
vu_set_fd_handler (int fd, struct vu_fs_discipline * vtable)
{
  if (fd >= n_fd_handlers)
    {
      fd_handlers = ((struct vu_fs_discipline **)
		     xrealloc (fd_handlers,
			       (fd + 1) * sizeof (struct vu_fs_discipline *)));
      memset ((char *)(fd_handlers + n_fd_handlers),
	      0,
	      (1 + fd - n_fd_handlers) * sizeof (struct vu_fs_discipline *));
      n_fd_handlers = fd + 1;
    }
  fd_handlers[fd] = vtable;
}


/* Dispatching on file names and descriptor numbers.
 */


/* This defines a trivial fstem implementation in which 
 * all functions return EINVAL is used as the default
 * implementation.  This implementation is the implementation of
 * last resort for the dispatcher.
 */
static int
bad_arg_handler (int * errn, ...)
{
  *errn = EINVAL;
  return -1;
}

static struct vu_fs_discipline bad_arg_handlers \
  = { VU_MAP2_FS_NAMES (_D, , , ) };

struct vu_fs_discipline *
vu_path_dispatch (char * path)
{
  int x;
  for (x = n_fs_handlers - 1; x >= 0; --x)
    {
      if (!regexec (fs_handlers[x].preg, path, 0, 0, fs_handlers[x].eflags))
	return fs_handlers[x].vtable;
    }
  return &bad_arg_handlers;
}

struct vu_fs_discipline *
vu_fd_dispatch (int fd)
{
  if ((fd < 0) || (fd > n_fd_handlers) || !fd_handlers[fd])
    return &bad_arg_handlers;

  return fd_handlers[fd];
}

struct vu_fs_discipline *
vu_dir_dispatch (DIR * dir)
{
  return vu_fd_dispatch (dir->dd_fd);
}



/* VU call front end.
 *
 * This approximately mirrors a traditional unix system call
 * interface, but has three improvments:
 *
 * 	0. No parameters are declared const.
 * 	1. Error numbers are not stored in a global, but
 *	   in a return value.
 *	2. chmod is spelled chmode.
 */

int
vu_access (int * errn, char * path, int mode)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->access(errn, path, mode);
}


int
vu_chdir (int * errn, char * path)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->chdir(errn, path);
}


int
vu_chmode (int * errn, char * path, int mode)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->chmode(errn, path, mode);
}


int
vu_chown (int * errn, char * path, int owner, int group)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->chown(errn, path, owner, group);
}


int
vu_chroot (int * errn, char * path)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->chroot(errn, path);
}


int
vu_close (int * errn, int fd)
{
  struct vu_fs_discipline * vt;
  int status;

  vt = vu_fd_dispatch (fd);
  status = vt->close(errn, fd);
  if (status >= 0)
    {
      fd_handlers [fd] = 0;
    }
  return status;
}

int
vu_closedir (int * errn, DIR * dir)
{
  struct vu_fs_discipline * vt;
  int fd;
  int status;

  vt = vu_dir_dispatch (dir);
  fd = dir->dd_fd;
  status = vt->closedir(errn, dir);
  if (status >= 0)
    {
      fd_handlers [fd] = 0;
    }
  return status;
}


int
vu_fchdir (int * errn, int fd)
{
  struct vu_fs_discipline * vt;

  vt = vu_fd_dispatch (fd);
  return vt->fchdir(errn, fd);
}


int
vu_fstat (int * errn, int fd, struct stat * buf)
{
  struct vu_fs_discipline * vt;

  vt = vu_fd_dispatch (fd);
  return vt->fstat(errn, fd, buf);
}

int
vu_fsync (int * errn, int fd)
{
  struct vu_fs_discipline * vt;

  vt = vu_fd_dispatch (fd);
  return vt->fsync(errn, fd);
}

int
vu_ftruncate (int * errn, int fd, int where)
{
  struct vu_fs_discipline * vt;

  vt = vu_fd_dispatch (fd);
  return vt->ftruncate(errn, fd, where);
}


int
vu_link (int * errn, char * from, char * to)
{
  struct vu_fs_discipline * vt;
  struct vu_fs_discipline * vt2;

  vt = vu_path_dispatch (from);
  vt2 = vu_path_dispatch (to);
  if (vt != vt2)
    {
      *errn = EXDEV;
      return -1;
    }
  return vt->link(errn, from, to);
}


int
vu_lseek (int * errn, int fd, int offset, int whence)
{
  struct vu_fs_discipline * vt;

  vt = vu_fd_dispatch (fd);
  return vt->lseek(errn, fd, offset, whence);
}


int
vu_lstat (int * errn, char * path, struct stat * buf)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->lstat(errn, path, buf);
}


int
vu_mkdir (int * errn, char * path, int mode)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->mkdir(errn, path, mode);
}


int
vu_open (int * errn, char * path, int flags, int mode)
{
  struct vu_fs_discipline * vt;
  int fd;

  vt = vu_path_dispatch (path);
  fd = vt->open(errn, path, flags, mode);
  if (   (fd >= 0)
      && (   (n_fd_handlers <= fd)
	  || !fd_handlers [fd]))
    vu_set_fd_handler (fd, vt);
  return fd;
}


int
vu_opendir (int * errn, DIR ** retv,  char * path)
{
  struct vu_fs_discipline * vt;
  int fd;
  int status;

  vt = vu_path_dispatch (path);
  status = vt->opendir(errn, retv, path);
  if (status >= 0)
    {
      fd = (*retv)->dd_fd;
      if (  (fd >= 0)
	  && (   (n_fd_handlers <= fd)
	      || !fd_handlers [fd]))
	vu_set_fd_handler (fd, vt);
    }
  return status;
}


int
vu_read (int * errn, int fd, char * buf, int count)
{
  struct vu_fs_discipline * vt;

  vt = vu_fd_dispatch (fd);
  return vt->read(errn, fd, buf, count);
}


int
vu_readdir (int * errn, struct dirent * retv, DIR * dir)
{
  struct vu_fs_discipline * vt;

  vt = vu_dir_dispatch (dir);
  return vt->readdir(errn, retv, dir);
}


int
vu_readlink (int * errn, char * path, char * buf, int bufsize)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->readlink(errn, path, buf, bufsize);
}


int
vu_rename (int * errn, char * from, char * to)
{
  struct vu_fs_discipline * vt;
  struct vu_fs_discipline * vt2;

  vt = vu_path_dispatch (from);
  vt2 = vu_path_dispatch (to);
  if (vt != vt2)
    {
      *errn = EXDEV;
      return -1;
    }
  return vt->rename(errn, from, to);
}


int
vu_rmdir (int * errn, char * path)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->rmdir(errn, path);
}


int
vu_seekdir (int * errn, DIR * dir, int offset)
{
  struct vu_fs_discipline * vt;

  vt = vu_dir_dispatch (dir);
  return vt->seekdir(errn, dir, offset);
}


int
vu_stat (int * errn, char * path, struct stat * buf)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->stat(errn, path, buf);
}


int
vu_symlink (int * errn, char * from, char * to)
{
  struct vu_fs_discipline * vt;
  struct vu_fs_discipline * vt2;

  vt = vu_path_dispatch (from);
  vt2 = vu_path_dispatch (to);
  if (vt != vt2)
    {
      *errn = EXDEV;
      return -1;
    }
  return vt->symlink(errn, from, to);
}


int
vu_telldir (int * errn, DIR * dir)
{
  struct vu_fs_discipline * vt;

  vt = vu_dir_dispatch (dir);
  return vt->telldir(errn, dir);
}


int
vu_truncate (int * errn, char * path, int where)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->truncate(errn, path, where);
}


int
vu_unlink (int * errn, char * path)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->unlink(errn, path);
}


int
vu_utimes (int * errn, char * path, struct timeval * tvp)
{
  struct vu_fs_discipline * vt;

  vt = vu_path_dispatch (path);
  return vt->utimes (errn, path, tvp);
}


int
vu_write (int * errn, int fd, char * buf, int count)
{
  struct vu_fs_discipline * vt;

  vt = vu_fd_dispatch (fd);
  return vt->write(errn, fd, buf, count);
}

int
vu_write_rtry (int * errn, int fd, char * buf, int count)
{
  int orig_count;
  orig_count = count;
  while (1)
    {
      int amt;
      amt = vu_write (errn, fd, buf, count);
      if (amt < 0)
	return amt;
      if (amt == count)
	return orig_count;
      count -= amt;
      buf += amt;
    }
}


int
vu_fcntl (int * errn, int fd, int cmd, long arg)
{
  struct vu_fs_discipline * vt;

  vt = vu_fd_dispatch (fd);
  return vt->fcntl(errn, fd, cmd, arg);
}


int
vu_ioctl (int * errn, int fd, int request, void * params)
{
  struct vu_fs_discipline * vt;

  vt = vu_fd_dispatch (fd);
  return vt->ioctl(errn, fd, request, params);
}


int
vu_dup (int * errn, int fd)
{
  struct vu_fs_discipline * vt;
  int new_fd;

  vt = vu_fd_dispatch (fd);
  new_fd = vt->dup(errn, fd);
  
  return new_fd;
}




int
vu_dup2 (int * errn, int fd, int newfd)
{
  struct vu_fs_discipline * vt;
  int new_fd;

  vt = vu_fd_dispatch (fd);
  new_fd = vt->dup2(errn, fd, newfd);
  return new_fd;
}



/* This defines a trivial fstem implementation in which 
 * all functions call their OS equivelents.
 */

struct vu_fs_discipline vu_system_fs_vtable \
  = { VU_FS_DISCIPLINE_INITIALIZERS (vu_sys_) };

int
vu_sys_access (int * errn, char * path, int mode)
{
  int rv;
  rv = access (path, mode);
  *errn = errno;
  return rv;
}


int
vu_sys_chdir (int * errn, char * path)
{
  int rv;
  rv = chdir (path);
  *errn = errno;
  return rv;
}


int
vu_sys_chmode (int * errn, char * path, int mode)
{
  int rv;
  rv = chmod (path, mode);
  *errn = errno;
  return rv;
}


int
vu_sys_chown (int * errn, char * path, int owner, int group)
{
  int rv;
  rv = chown (path, owner, group);
  *errn = errno;
  return rv;
}


int
vu_sys_chroot (int * errn, char * path)
{
  int rv;
  rv = chroot (path);
  *errn = errno;
  return rv;
}


int
vu_sys_closedir (int * errn, DIR * dir)
{
  int rv;
  rv = closedir (dir);
  *errn = errno;
  return rv;
}


int
vu_sys_close (int * errn, int fd)
{
  int rv;
  rv = close (fd);
  *errn = errno;
  return rv;
}


int
vu_sys_fchdir (int * errn, int fd)
{
  int rv;
  rv = fchdir (fd);
  *errn = errno;
  return rv;
}


int
vu_sys_fstat (int * errn, int fd, struct stat * buf)
{
  int rv;
  rv = fstat (fd, buf);
  *errn = errno;
  return rv;
}

int
vu_sys_fsync (int * errn, int fd)
{
  int rv;
  rv = fsync (fd);
  *errn = errno;
  return rv;
}

int
vu_sys_ftruncate (int * errn, int fd, int where)
{
  int rv;
  rv = ftruncate (fd, where);
  *errn = errno;
  return rv;
}


int
vu_sys_link (int * errn, char * from, char * to)
{
  int rv;
  rv = link (from, to);
  *errn = errno;
  return rv;
}


int
vu_sys_lseek (int * errn, int fd, int offset, int whence)
{
  int rv;
  rv = lseek (fd, offset, whence);
  *errn = errno;
  return rv;
}


int
vu_sys_lstat (int * errn, char * path, struct stat * buf)
{
  int rv;
  rv = lstat (path, buf);
  *errn = errno;
  return rv;
}


int
vu_sys_mkdir (int * errn, char * path, int mode)
{
  int rv;
  rv = mkdir (path, mode);
  *errn = errno;
  return rv;
}


int
vu_sys_open (int * errn, char * path, int flags, int mode)
{
  int rv;
  rv = open (path, flags, mode);
  *errn = errno;
  if (rv >= 0)
    vu_set_fd_handler (rv, &vu_system_fs_vtable);
  return rv;
}


int
vu_sys_opendir (int * errn, DIR ** retv,  char * path)
{
  *retv = opendir (path);
  *errn = errno;
  if (*retv != 0)
    vu_set_fd_handler ((*retv)->dd_fd, &vu_system_fs_vtable);
  return (*retv == 0 ? -1 : 0);
}

int
vu_sys_read (int * errn, int fd, char * buf, int count)
{
  int rv;
  rv = read (fd, buf, count);
  *errn = errno;
  return rv;
}


int
vu_sys_readdir (int * errn, struct dirent * retv, DIR * dir)
{
  struct dirent * de;
  de = readdir (dir);
  *errn = errno;
  if (!de)
    return -1;
  *retv = *de;
  return 0;
}

int
vu_sys_readlink (int * errn, char * path, char * buf, int bufsize)
{
  int rv;
  rv = readlink (path, buf, bufsize);
  *errn = errno;
  return rv;
}

int
vu_sys_rename (int * errn, char * from, char * to)
{
  int rv;
  rv = rename (from, to);
  *errn = errno;
  return rv;
}

int
vu_sys_rmdir (int * errn, char * path)
{
  int rv;
  rv = rmdir (path);
  *errn = errno;
  return rv;
}


int
vu_sys_seekdir (int * errn, DIR * dir, int offset)
{
  int rv;
  seekdir (dir, offset);
  *errn = 0;
  return 0;
}

int
vu_sys_stat (int * errn, char * path, struct stat * buf)
{
  int rv;
  rv = stat (path, buf);
  *errn = errno;
  return rv;
}


int
vu_sys_symlink (int * errn, char * from, char * to)
{
  int rv;
  rv = symlink (from, to);
  *errn = errno;
  return rv;
}


int
vu_sys_telldir (int * errn, DIR * dir)
{
  int rv;
  rv = telldir (dir);
  *errn = errno;
  return rv;
}


int
vu_sys_truncate (int * errn, char * path, int where)
{
  int rv;
  rv = truncate (path, where);
  *errn = errno;
  return rv;
}


int
vu_sys_unlink (int * errn, char * path)
{
  int rv;
  rv = unlink (path);
  *errn = errno;
  return rv;
}


int
vu_sys_utimes (int * errn, char * path, struct timeval * tvp)
{
  int rv;
  rv = utimes (path, tvp);
  *errn = errno;
  return rv;
}


int
vu_sys_write (int * errn, int fd, char * buf, int count)
{
  int rv;
  rv = write (fd, buf, count);
  *errn = errno;
  return rv;
}


int
vu_sys_fcntl (int * errn, int fd, int cmd, long arg)
{
  int rv;
  rv = fcntl (fd, cmd, arg);
  *errn = errno;
  return rv;
}


int
vu_sys_ioctl (int * errn, int fd, int request, void * params)
{
  int rv;
  rv = ioctl (fd, request, params);
  *errn = errno;
  return rv;
}


int
vu_sys_dup (int * errn, int fd)
{
  int rv;
  rv = dup (fd);
  *errn = errno;
  return rv;
}


int
vu_sys_dup2 (int * errn, int fd, int newfd)
{
  int rv;
  rv = dup2 (fd, newfd);
  *errn = errno;
  return rv;
}



