/*
 *  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.
 *
 *  stat.c,v 1.1.1.1 1994/04/04 04:30:35 amiga Exp
 *
 *  stat.c,v
 * Revision 1.1.1.1  1994/04/04  04:30:35  amiga
 * Initial CVS check in.
 *
 *  Revision 1.2  1993/11/05  22:03:10  mwild
 *  grp/oth support, plus new feature
 *
 *  Revision 1.1  1992/05/14  19:55:40  mwild
 *  Initial revision
 *
 */

#define KERNEL
#include "ixemul.h"
#include "kprintf.h"

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

#ifndef ST_ROOT
#define ST_ROOT		1
#define ST_USERDIR 	2
#define ST_SOFTLINK	3
#define ST_LINKDIR	4
#define ST_FILE		-3
#define ST_LINKFILE	-4
#endif

#ifndef ERROR_IS_SOFT_LINK
#define ERROR_IS_SOFT_LINK  233
#endif

/* currently, links are quite buggy.. hope this get cleaned up RSN ;-) */

extern BPTR __lock (), __llock ();

static int
__stat(char *fname, struct stat *stb, BPTR (*lock_func)())
{
  BPTR lock;
  long mode;
  struct FileInfoBlock *fib;
  struct InfoData *info;
  int omask;
  int error;

  bzero (stb, sizeof(*stb));

  omask = syscall (SYS_sigsetmask, ~0);

  if (!(lock = (*lock_func) (fname, ACCESS_READ)))
    {
      int err = IoErr ();

      /* take special care of NIL:, /dev/null and friends ;-) */
      if (err == 4242)
        {
          stb->st_mode = S_IFCHR | 0777;
          stb->st_nlink = 1;
          stb->st_blksize = ix.ix_fs_buf_factor * 512;
          stb->st_blocks = 0;
          goto rest_sigmask;
        }

      KPRINTF (("__stat: lock %s failed, err = %ld.\n", fname, err));
      if (err == ERROR_IS_SOFT_LINK)
	{
	  /* provide some default stb.. we can't get anymore info than
	   * that. Symlinks should work with Lock(), but till now they
	   * don't ;-( */
	  stb->st_handler = (int) DeviceProc ((UBYTE *)fname);
	  stb->st_dev = (dev_t) stb->st_handler;
	  /* HELP! no way to reach the fib of this entry except when
	   * scanning the parent directory, but this is NOT acceptable ! */
	  stb->st_ino = 123;
	  /* this is the most important entry ;-) */
	  stb->st_mode = S_IFLNK | 0777;
	  stb->st_size = 1024; /* again, this should be availabe... */
	  stb->st_nlink = 1;
	  stb->st_uid = stb->st_gid = stb->st_rdev = 0;
	  /* again, these values ARE accessible, but only by ExNext */
	  stb->st_atime = stb->st_mtime = stb->st_ctime = 0;
	  stb->st_blksize = stb->st_blocks = 0;
	  goto rest_sigmask;
        }
error:
      syscall (SYS_sigsetmask, omask);
      errno = __ioerr_to_errno (err);
      KPRINTF (("ioerr = %ld, &errno = %lx, errno = %ld\n", err, &errno, errno));
      return -1;
    }
  KPRINTF (("__stat: lock %s ok.\n",fname));

  /* alloca() returns stack memory, so it's guaranteed to be word 
   * aligned (anything else would be deadly for the sp...) Since DOS needs
   * long aligned data, we'll allocate 1 word more and adjust as needed */
  fib = alloca (sizeof(*fib) + 2);
  /* DON'T use LONG_ALIGN(alloca(..)), the argument is evaluated more than once! */
  fib = LONG_ALIGN (fib);

  info = alloca (sizeof(*info) + 2);
  info = LONG_ALIGN (info);

  if (!(Examine (lock, fib)))
    {
      __unlock (lock);
      goto error;
    }

  stb->st_nlink = 1; /* always one link entry per file, unless... */

  mode = 0L;
  stb->st_amode = fib->fib_Protection;
  if (!(fib->fib_Protection & FIBF_EXECUTE))
    mode |= S_IXUSR;
  if (!(fib->fib_Protection & FIBF_WRITE))
    mode |= S_IWUSR;
  if (!(fib->fib_Protection & FIBF_READ))
    mode |= S_IRUSR;
#ifdef FIBF_GRP_EXECUTE
  /* FIBF_GRP_EXECUTE requires at least OS3 headers */
  if (!(fib->fib_Protection & FIBF_GRP_EXECUTE))
    mode |= S_IXGRP;
  if (!(fib->fib_Protection & FIBF_GRP_WRITE))
    mode |= S_IWGRP;
  if (!(fib->fib_Protection & FIBF_GRP_READ))
    mode |= S_IRGRP;
  if (!(fib->fib_Protection & FIBF_OTR_EXECUTE))
    mode |= S_IXOTH;
  if (!(fib->fib_Protection & FIBF_OTR_WRITE))
    mode |= S_IWOTH;
  if (!(fib->fib_Protection & FIBF_OTR_READ))
    mode |= S_IROTH;
#endif

  switch (fib->fib_DirEntryType)
    {
    case ST_LINKDIR:
      stb->st_nlink ++;  /* we never get more than a link count of two.. */
    case ST_ROOT:
    case ST_USERDIR:
      mode |= S_IFDIR;
      break;

    /* at the moment, we NEVER get this entry, since we can't get a lock
     * on a symlink */
    case ST_SOFTLINK:
      mode |= S_IFLNK;
      break;

    case ST_LINKFILE:
      stb->st_nlink ++;
    case ST_FILE:
    default:
      mode |= S_IFREG;
    }

  stb->st_mode = mode;
  
  /* support for annotated attributes (cool name ;-)) */
  if (! strncmp (fib->fib_Comment, "!SP!", 4))
    {
      int sp_mode, sp_addr;

      if (sscanf (fib->fib_Comment + 4, "%x!%x", &sp_mode, &sp_addr) == 2)
        stb->st_mode = sp_mode;
    }
  
  /* some kind of a default-size for directories... */
  stb->st_size = fib->fib_DirEntryType<0 ? fib->fib_Size : 1024; 
  stb->st_handler = (long)((struct FileLock *)((long)lock << 2))->fl_Task;
  stb->st_dev = (dev_t)stb->st_handler; /* trunc to 16 bit */
  stb->st_ino = fib->fib_DiskKey; 
  stb->st_atime =
  stb->st_ctime =
  stb->st_mtime = (8*365+2)*24*3600 + /* offset to unix-timesystem */
		  fib->fib_Date.ds_Days * 24 * 60 * 60 +
		  fib->fib_Date.ds_Minute * 60 +
		  fib->fib_Date.ds_Tick/TICKS_PER_SECOND;
  /* in a try to count the blocks used for filemanagement, we add one for
   * the fileheader. Note, that this is wrong for large files, where there
   * are some extension-blocks as well */
  stb->st_blocks = fib->fib_NumBlocks + 1;
  
  bzero (info, sizeof (*info));
  stb->st_blksize = 0;
  if (Info(lock, info))
    /* optimal for fileio is as high as possible ;-) This is a
     * compromise between "as high as possible" and not too restricitve
     * for people low on memory */
    stb->st_blksize = info->id_BytesPerBlock * ix.ix_fs_buf_factor;

  if (! stb->st_blksize) stb->st_blksize = 512;

  __unlock (lock);

rest_sigmask:
  syscall (SYS_sigsetmask, omask);
  errno = 0;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return 0;
}

/***************************************************************************/

int
stat (char *fname, struct stat *stb)
{
  return __stat (fname, stb, __lock);
}

int
lstat (char *fname, struct stat *stb)
{
  return __stat (fname, stb, __llock);
}

