/*
 *  This file is part of ixemul.library for the Amiga.
 *  Copyright (C) 1991, 1992  Markus M. Wild
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#define KERNEL
#include "ixemul.h"

#undef DEBUG

#ifdef DEBUG
#define DP(a) kprintf a
#else
#define DP(a)
#endif

/* don't need to use the async port, no more than 1 task will be in here
 * at a time and use our packet */
#define __srwport (u.u_sync_mp)

extern int __mread (), __mclose ();

extern void *kmalloc (size_t), *krealloc (void *, size_t);

#if __GNUC__ != 2
#define alloca __builtin_alloca
#endif

#define BUFINCR	512

/* we're writing records that look like this to the `file' */
struct file_dir {
  long	fd_key;
  long	fd_namelen;	/* padded to even size, thus perhaps zero padded */
  char	fd_name[0];	/* fd_namelen bytes of filename */
};

/*
 * Convert a directory into a DTYPE_MEM file.
 *
 * NOTE: function assumes:
 *	 o f is allocated and locked, it's packet is initialized
 *       o name is an existing directory (S_IFDIR from a previous stat)
 *	 o signals are blocked
 */

int
convert_dir (struct file *f, char *name)
{
  BPTR lock;
  u_char *buf, *bp, *bend;
  int  buf_size;
  struct FileInfoBlock *fib;
  struct StandardPacket *sp;
  struct MsgPort *handler;
  struct file_dir *fd;
  int len, res, omask, err;

  fib = alloca (sizeof (*fib) + 2);
  fib = LONG_ALIGN (fib);
  
  res = 0;
  omask = syscall (SYS_sigsetmask, ~0);
  if ((lock = __lock (name, ACCESS_READ)) > 0)
    {
      buf = kmalloc (BUFINCR);
      buf_size = BUFINCR;
      bp = buf;
      bend = buf + buf_size;
      sp = &f->f_sp;
      handler = ((struct FileLock *)(BTOCPTR (lock))) -> fl_Task;
      
      sp->sp_Pkt.dp_Port = __srwport;
      sp->sp_Pkt.dp_Type = ACTION_EXAMINE_OBJECT;
      sp->sp_Pkt.dp_Arg1 = lock;
      sp->sp_Pkt.dp_Arg2 = CTOBPTR (fib);
      PutPacket (handler, sp);
      __wait_sync_packet (sp);

      /* put two dummy entries here.. some BSD code relies on the fact that
       * it can safely skip the first two `.' and `..' entries ;-)) */
      fd = (struct file_dir *) bp;
      fd->fd_key = 1;
      fd->fd_namelen = 2;
      fd->fd_name[0] = '.'; fd->fd_name[1] = 0;
      bp += fd->fd_namelen + sizeof (struct file_dir);
      fd = (struct file_dir *) bp;
      fd->fd_key = 2;
      fd->fd_namelen = 2;
      fd->fd_name[0] = '.'; fd->fd_name[1] = '.';
      bp += fd->fd_namelen + sizeof (struct file_dir);
      
      /* don't include the dir-information into the file, *ix doesn't either. */
      if (sp->sp_Pkt.dp_Res1 == -1)
	for (;;)
	  {
	    /* allow for a clean abort out of a very long directory scan */
	    if (u.p_sig & ~omask)
	      {
		/* if a signal is pending that is otherwise not blocked,
		 * break here, and return with EINTR */
		res = -1;
		err = EINTR;
		kfree (buf);
		bp = buf = 0;
		break;
	      }

	    sp->sp_Pkt.dp_Port = __srwport;
	    sp->sp_Pkt.dp_Type = ACTION_EXAMINE_NEXT;
	    sp->sp_Pkt.dp_Arg1 = lock;
	    sp->sp_Pkt.dp_Arg2 = CTOBPTR (fib);
	    PutPacket (handler, sp);
	    __wait_sync_packet (sp);
	    
    	    if (sp->sp_Pkt.dp_Res1 != -1) break;

	    /* tricky tricky... if working with packets, names are BSTRs... */
    	    len = fib->fib_FileName[0];

#if 0
/* this was just a debugging aid */
	    if ((unsigned)len > 30)
	      ix_panic ("ixemul.library: too long name (> 30 characters)!");
#endif

    	    if ((bp + (len + 2 + sizeof (struct file_dir))) >= bend)
	      {
	        u_char *tmp;

DP(("cd: buf = $%lx, bp = $%lx, bend = $%lx, new = $%lx.\n", buf, bp, bend, len + 1 + sizeof (fd)));

	        tmp = krealloc (buf, buf_size + BUFINCR);
	        if (! tmp)
		  {
		    ix_panic ("ixemul.library: out of memory!");
		    res = -1;
		    /* this way we get a zero length directory */
		    kfree (buf);
		    bp = buf = 0;
		    err = ENOMEM;
		    break;
		  }

	        buf_size += BUFINCR;
	        bp = tmp + (bp-buf);
	        buf = tmp; 
		bend = tmp + buf_size;
DP(("cd2: buf = $%lx, bp = $%lx, bend = $%lx, new = $%lx.\n", buf, bp, bend, len + 1 + sizeof (fd)));
	      }

	    fd = (struct file_dir *) bp;
    	    fd->fd_key = fib->fib_DiskKey;
    	    fd->fd_namelen = len;
    	    /* watch out for mc68000: don't let bp ever get odd ! */
    	    if (fd->fd_namelen & 1) 
	      /* in that case zero pad the name */
	      fd->fd_name[fd->fd_namelen++] = 0;

	    bcopy (fib->fib_FileName + 1, fd->fd_name, len);
	    bp += fd->fd_namelen + sizeof (struct file_dir);
#ifdef DEBUG
	    if (bp >= bend || & fd->fd_name[len] >= bend)
	      {
	      	DP(("cd: OVERFLOW! bp = $%lx, bend = $%lx, len = $%lx, &fd->fd_name[len] = $%lx\n",
		    bp, bend, len, & fd->fd_name[len]));
	      }
#endif
	  }
      
      /* fine.. fill out the memory file object */
      f->f_type		= DTYPE_MEM;
      f->f_mf.mf_offset = 0;
      f->f_mf.mf_buffer = buf;
      f->f_read		= __mread;
      f->f_close	= __mclose;
      f->f_ioctl	= 0;
      f->f_select	= 0;
      f->f_stb.st_size	= bp-buf;
      /* NOTE: the rest of the stb should be ok from the previous stat() in
       *       open() */

      __unlock (lock);
    }
  else
    res = -1;

  syscall (SYS_sigsetmask, omask);
  
  errno = err;
  return res;
}
