/*
 *  This file is part of ixemul.library for the Amiga.
 *  Copyright (C) 1991, 1992  Markus M. Wild
 *  Portions Copyright (C) 1994 Rafael W. Luebbert
 *
 *  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.
 *
 *  $Id: convert_dir.c,v 1.4 1994/06/19 15:09:22 rluebbert Exp $
 *
 *  $Log: convert_dir.c,v $
 *  Revision 1.4  1994/06/19  15:09:22  rluebbert
 *  *** empty log message ***
 *
 *  Revision 1.2  1992/07/28  00:42:36  mwild
 *  fix signals, fix memory leak, fix possible never unlocked lock
 *
 *  Revision 1.1  1992/05/14  19:55:40  mwild
 *  Initial revision
 *
 */

#define KERNEL
#include "ixemul.h"
#include "kprintf.h"
#include <string.h>

extern int __mread (), __mclose ();

#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 (important! convert_dir() doesn't block them!)
 */

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

  fib = alloca (sizeof (*fib) + 2);
  fib = LONG_ALIGN (fib);
  
  res = 0;
  if ((lock = __lock (name, ACCESS_READ)) > 0)
    {
      buf = kmalloc (BUFINCR);
      buf_size = BUFINCR;
      bp = buf;
      bend = buf + buf_size;

      /* make sure we got that memory */
      if (! buf)
        {
          err = ENOMEM;
          res = -1;
          goto do_return;
        }
      
      rc0 = Examine (lock, fib);

      /* 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 (rc0 == -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 was not blocked before entry to
		 * open(), break here and return with EINTR */
		res = -1;
		err = EINTR;
		kfree (buf);
		goto do_return;
	      }

	    rc0 = ExNext (lock, fib);

    	    if (rc0 != -1)
	      break;

    	    len = strlen (fib->fib_FileName);

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

	        tmp = krealloc (buf, buf_size + BUFINCR);
	        if (!tmp)
		  {
		    ix_panic ("ixemul.library: out of memory!");
		    res = -1;
		    kfree (buf);
		    err = ENOMEM;
		    goto do_return;
		  }

	        buf_size += BUFINCR;
	        bp = tmp + (bp-buf);
	        buf = tmp; 
		bend = tmp + buf_size;
	      }

	    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, fd->fd_name, len);
	    bp += fd->fd_namelen + sizeof (struct file_dir);
#ifdef DEBUG
	    if (bp >= bend || & fd->fd_name[len] >= bend)
	      {
	      	KPRINTF (("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() */
    }
  else
    {
      errno = ENOENT;
      KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
      return -1;
    }

  /* NOTE: granted, this is a bit spaghetti here.. the else above guarantees that
           we won't unlock a lock we never got. So it's safe to unconditionally
           unlock at the end. */

do_return:
  __unlock (lock);
  errno = err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return res;
}
