/* ,file-id archive://[lord]/408/vu/./vustr.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 <errno.h>
#include <fcntl.h>
#include "ar.h"
#include "fname.h"
#include "rx/regex.h"
#include "vu.h"
#include "vustr.h"
#include "xmalloc.h"



static struct vu_fs_discipline vustr_vtable;



struct string_fd
{
  char * str;
  int posn;
  int flags;
  int permit_resize;
  int require_free;
  struct stat stat;
};

static struct string_fd * strs = 0;



static void
put_string_on_fd (int fd, char * str, int size, int flags, int pr, int rf)
{
  struct string_fd * r;
  r = (struct string_fd *)aref ((void **)&strs, fd, sizeof (*r));
  r->str = str;
  r->posn = 0;
  r->flags = flags;
  r->permit_resize = pr;
  r->require_free = rf;
  r->stat.st_dev = 0;
  r->stat.st_ino = 0;
  r->stat.st_mode = 0;
  r->stat.st_nlink = 0;
  r->stat.st_uid = 0;
  r->stat.st_gid = 0;
  r->stat.st_rdev = 0;
  r->stat.st_size = size;
  r->stat.st_atime = time (0);
  r->stat.st_mtime = r->stat.st_atime;
  r->stat.st_ctime = r->stat.st_atime;
}

int
open_string (int * errn,
	     char * str,
	     int size,
	     int flags,
	     int permit_resize,
	     int require_free)
{
  int fd;
  
  fd = reserv (errn, O_RDONLY);
  if (fd < 0)
    return -1;
  put_string_on_fd (fd, str, size, flags, permit_resize, require_free);
  vu_set_fd_handler (fd, &vustr_vtable);
  return fd;
}

int
fd_string (char ** ret, int * szret, int * errn, int fd)
{
  struct string_fd * r;
  
  r = (struct string_fd *)aref ((void **)&strs, fd, sizeof (*r));
  *ret = r->str;
  *szret = r->stat.st_size;
  return r->posn;
}


int
vustr_open (int * errn, char * path, int flags, int mode)
{
  char * tail;
  int permit_resize;
  int require_free;
  int tail_len;
  char * str;
  int size;
  
  tail = file_name_tail (path);

  permit_resize = (*tail == 'r');
  if (permit_resize)
    ++tail;

  require_free = (*tail == 'm');
  if (require_free)
    ++tail;

  tail_len = strlen (tail);
  size = scan_uhex (&tail, &tail_len, 0);

  if (*tail != ',')
    {
      *errn = ENOENT;
      return -1;
    }

  str = (char *)scan_uhex (&tail, &tail_len, 0);

  return open_string (errn, str, size, flags, permit_resize, require_free);
}



int
vustr_access (int * errn, char * path, int mode)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_chdir (int * errn, char * path)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_chmode (int * errn, char * path, int mode)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_chown (int * errn, char * path, int owner, int group)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_chroot (int * errn, char * path)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_closedir (int * errn, DIR * dir)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_close (int * errn, int fd)
{
  struct string_fd * r;
  
  r = (struct string_fd *)aref ((void **)&strs, fd, sizeof (*r));
  unreserv (fd);

  if (r->require_free)
    xfree (r->str);

  return 0;
}

int
vustr_fchdir (int * errn, int fd)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_fstat (int * errn, int fd, struct stat * buf)
{
  struct string_fd * r;
  
  r = (struct string_fd *)aref ((void **)&strs, fd, sizeof (*r));
  *buf = r->stat;
  return 0;
}

int
vustr_fsync (int * errn, int fd)
{
  return 0;
}

int
vustr_ftruncate (int * errn, int fd, int where)
{
  struct string_fd * r;
  
  r = (struct string_fd *)aref ((void **)&strs, fd, sizeof (*r));
  if (r->stat.st_size > where)
    {
      r->stat.st_size = where;
      if (r->posn > where)
	r->posn = where;
      return 0;
    }
  else if (r->permit_resize)
    {
      r->str = xrealloc ((void *)r->posn, where);
      r->stat.st_size = where;
      return 0;
    }
  else
    {
      *errn = EBADFD;
      return -1;
    }
}

int
vustr_link (int * errn, char * from, char * to)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_lseek (int * errn, int fd, int offset, int whence)
{
  struct string_fd * r;
  int goal;
  
  r = (struct string_fd *)aref ((void **)&strs, fd, sizeof (*r));
  switch (whence)
    {
    default:
    case SEEK_SET:
      goal = offset;
      break;
    case SEEK_CUR:
      goal = offset + r->posn;
      break;
    case SEEK_END:
      goal = r->stat.st_size + r->posn;
      break;
    }

  if (goal < 0)
    goal = 0;

  if (goal > r->stat.st_size)
    {
      if (r->permit_resize)
	{
	  r->str = xrealloc ((void *)r->posn, goal);
	  r->stat.st_size = goal;
	  r->posn = goal;
	  return 0;
	}
      else
	{
	  *errn = ENOSPC;
	  return -1;
	}
    }

  r->posn = goal;
  return 0;
}

int
vustr_lstat (int * errn, char * path, struct stat * buf)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_mkdir (int * errn, char * path, int mode)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_opendir (int * errn, DIR ** retv, char * path)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_read (int * errn, int fd, char * buf, int count)
{
  int avail;
  int amt;
  struct string_fd * r;
  
  r = (struct string_fd *)aref ((void **)&strs, fd, sizeof (*r));
  avail = r->stat.st_size - r->posn;
  amt = (count < avail ? count : avail);
  memcpy (buf, r->str + r->posn, amt);
  r->posn += amt;
  return amt;
}

int
vustr_readdir (int * errn, struct dirent * retv, DIR * dir)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_readlink (int * errn, char * path, char * buf, int bufsize)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_rename (int * errn, char * from, char * to)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_rmdir (int * errn, char * path)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_seekdir (int * errn, DIR * dir, int offset)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_stat (int * errn, char * path, struct stat * buf)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_symlink (int * errn, char * from, char * to)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_telldir (int * errn, DIR * dir)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_truncate (int * errn, char * path, int where)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_unlink (int * errn, char * path)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_utimes (int * errn, char * path, struct timeval * tvp)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_write (int * errn, int fd, char * buf, int count)
{
  struct string_fd * r;
  int new_posn;
  int ign;
  
  r = (struct string_fd *)aref ((void **)&strs, fd, sizeof (*r));
  new_posn = r->posn + count;

  if (   (new_posn > r->stat.st_size)
      && (0 > vustr_ftruncate (&ign, fd, new_posn)))
    {
      count = r->stat.st_size - r->posn;
      new_posn = r->posn + count;
    }
  memcpy (r->str + r->posn, buf, count);
  return count;
}

int
vustr_fcntl (int * errn,
	     int fd,
	     int cmd,
	     long arg)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_ioctl (int * errn,
	     int fd,
	     int request,
	     void * params)
{
  *errn = ENOSYS;
  return -1;
}

int
vustr_dup (int * errn, int fd)
{
  struct string_fd * r;
  int size;
  char * str;
  struct string_fd * s;
  int newfd;
  
  r = (struct string_fd *)aref ((void **)&strs, fd, sizeof (*r));
  size = r->stat.st_size;
  str = xstrnsav (r->str, size);

  newfd = open_string (errn, str, size, r->flags, r->permit_resize, 1);
  if (newfd < 0)
    return newfd;

  s = (struct string_fd *)aref ((void **)&strs, newfd, sizeof (*r));

  s->posn = r->posn;
  return newfd;
}

int
vustr_dup2 (int * errn, int fd, int newfd)
{
  struct string_fd * r;
  int size;
  char * str;
  struct string_fd * s;
  
  if (newfd != vu_sys_dup2 (errn, fd, newfd))
    return -1;

  r = (struct string_fd *)aref ((void **)&strs, fd, sizeof (*r));
  size = r->stat.st_size;
  str = xstrnsav (r->str, size);

  put_string_on_fd (newfd, str, size, r->flags, r->permit_resize, 1);

  s = (struct string_fd *)aref ((void **)&strs, newfd, sizeof (*r));
  s->posn = r->posn;
  return newfd;
}



static struct vu_fs_discipline vustr_vtable 
  = { VU_FS_DISCIPLINE_INITIALIZERS (vustr_) };
