/****************************************************************************
 *
 * $Source: /cvsroot/riscossmbserver/smbserver/src/unix/c/stat,v $
 * $Date: 2000/04/02 17:00:31 $
 * $Revision: 1.9 $
 * $State: Exp $
 * $Author: david $
 *
 * $Log: stat,v $
 * Revision 1.9  2000/04/02 17:00:31  david
 * Changes to allow this to be compiled as a RISCOS module. Mainly
 * fix problems with stack depth which is limited in SVC mode.
 *
 * Revision 1.8  2000/02/19 16:33:23  david
 * fix to allow Xfile archives/dirs to be served correctly to !lanman98
 * clients
 *
 * Revision 1.7  2000/02/13 21:05:22  david
 * Modified following testing with !LanMan98
 *
 * Revision 1.6  2000/02/08 20:19:11  david
 * Added detect !lanman 98, detect extensions, tidied file type stuff,
 * changed duplicate filename delimeter (for files with same name but
 * different extension) from / to ALT+\
 *
 * Revision 1.5  2000/01/29 17:27:39  david
 * Changed somethiing?!
 *
 * Revision 1.4  1999/11/23 22:05:12  david
 * Increased filename length to MAXPATHLEN
 * uname and dirent - support for spaces in a filename which
 * is translated to 0xa0 on RISCOS and mapping of obscure DOS
 * filename characters to RICOS characters as 2-327 or the PRMs
 *
 * Revision 1.3  1999/07/20 21:01:08  david
 * Added calls to getFileName to find the file name.
 *
 * Revision 1.2  1999/06/20 13:49:06  david
 * Added change to check unmodified name if name returned
 * by uname fails.
 * ,
 *
 * Revision 1.1  1999/05/16 12:00:09  david
 * Initial revision
 *
 * Revision 1.5  1996/10/30 22:04:51  unixlib
 * Massive changes made by Nick Burret and Peter Burwood.
 *
 * Revision 1.4  1996/09/16 21:23:52  unixlib
 * CL_0002 Nick Burret
 * Minor changes to file handling
 * Change most error numbers, and use in assembler sources (SJC)
 * Various minor bug fixes and compatability changes.
 *
 * Revision 1.3  1996/07/21 22:12:31  unixlib
 * CL_0001 Nick Burret
 * Improve memory handling. Remove C++ library incompatibilities.
 * Improve file stat routines.
 *
 * Revision 1.2  1996/05/06 09:01:35  unixlib
 * Updates to sources made by Nick Burrett, Peter Burwood and Simon Callan.
 * Saved for 3.7a release.
 *
 * Revision 1.1  1996/04/19 21:35:27  simon
 * Initial revision
 *
 ***************************************************************************/

static const char rcs_id[] = "$Id: stat,v 1.9 2000/04/02 17:00:31 david Exp $";

#if 0
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/dev.h>
#include <sys/unix.h>
#include <sys/stat.h>
#include <sys/os.h>
#include <sys/swis.h>
   
#else

#include "includes.h"
#include "swis.h"
#include "my_os.h"
     
#define DEV_RISCOS	0	/* RiscOS file system */
#define DEV_TTY 	1	/* tty */
#define DEV_PIPE	2	/* UnixLib pipe() */
#define DEV_NULL	3	/* /dev/null */

#endif

extern char *getFileName(char *fname);

static void __stat (register int *, register struct stat *);

extern BOOL expandZips;
extern BOOL detectLanMan98;

int stat (const char *fname, struct stat *buf)
{
  int r[6];
  _kernel_oserror *e;
  char *file;
  int  failed=0;
  int  ftype;
  char ftypeS[5];  
  char *c;                                              
  
  DEBUG(3,("stat %s\n",fname));                                     
  
  if (!buf)
    {
      errno = EINVAL;
      DEBUG(3,("stat fails, errno %d\n",errno));
      return (-1);
    }
  

   file = getFileName(__uname ((char *)fname, 0)); 
  

  if (e = myos_file (0x05, file, r))
    {
     /* __seterr (e); */                 
      DEBUG(3,("stat (myos_file) fails, errno %d, %s\n",errno,strerror(errno)));
      errno = ENOENT;
      failed=1;
    }
  if (!r[0])
    {
      errno = ENOENT;
      DEBUG(3,("stat fails for %s, errno %d, %s\n",file,errno,strerror(errno)));
      failed=1;
    }
                           
  /* if LanMan98 enforce that the file has a file type appended ! */
  if (failed==0 &&  detectLanMan98==True && r[0]!=2 
      /* && isInDosDisc(file,False)==False*/)
  {
    ftype = (r[2] & 0x000fff00)>>8;
    if (ftype==0xb23 && r[0]==3)
    {
      ftype=0;
    }
    if (ftype!=0 && ftype!=0xfe4)
    {
      /* insist that lanman appends a file type */  
      sprintf(ftypeS,",%.3x",ftype); 
      c=strstr(fname,ftypeS);
      if (c==NULL)
      {
        /* file type not in file, try wild cards */
        c=strstr(fname,",???");
        if (c==NULL)
        {
          /* no file type appended so claim file does not exist */
          DEBUG(5,("file %s exists not does not have file type 0x%.3x appended\n",
                file, ftype));   
          return -1;
        }
      }
       
    }
  }
                                 
  /* try original name */
  if (failed==1)
  {  
 /* 
  * WARNING THIS TEST PASSES INCORRECTLY IF NAME BEGINS WITH 10 VALID CHARACTER DIRECTOR
  * NAME
  */
    failed=0;
    if (e = myos_file (0x05, (char*)fname, r))
    {
     /* __seterr (e); */                 
      DEBUG(3,("stat (myos_file) fails, errno %d, %s\n",errno,strerror(errno)));
      errno = ENOENT;
      failed=1;
    }
    if (!r[0])
    {
      errno = ENOENT;
      DEBUG(3,("stat fails for %s, errno %d, %s\n",fname,errno,strerror(errno)));
      failed=1;
    }      

    if (failed==1)
    {
      return -1;
    }    
    else
    {
       DEBUG(3,("stat succeeds for %s\n",fname));   
       checkForDosDisc((char*)fname,False,r[2]);
    }
  }
  else
  {
     DEBUG(3,("stat succeeds for %s\n",file)); 
     checkForDosDisc(file,False,r[2]);
  }
     
  buf->st_dev = makedev (DEV_RISCOS, 0);

  __stat (r, buf);

  return (0);
}

int lstat (const char *fname, struct stat *buf)
{
  DEBUG(3,("lstat\n"));

  return stat (fname, buf);
}

#ifdef fstat
#undef fstat
#endif

int my_fstat (int fd, struct stat *buf)
{
  register struct file *f;
  char filename[MAXPATHLEN+100];

  DEBUG(3,("my_fstat %d\n",fd));

  if (!buf)
    {
      errno = EINVAL;
      return (-1);
    }

/*  if (BADF (fd))
    {
      errno = EBADF;
      return (-1);
    }
*/
/*
  f = __u->file + fd;

  buf->st_dev = f->dev;

  __stat (f->r, buf);
*/
  /* removed & from before filename */  
  if (file_find_name(fd,filename)==EBADF)
  {
     errno = EBADF;
     return (-1);
  }                           
  DEBUG(5,("fstat %s\n",filename));
  
  return ( stat(filename,buf) );
}

/* This function is used by __stat() and readdir().  */

unsigned int __get_file_serial_no (char *fname)
{
  int r[10];
  char tmp[256], *s;
  unsigned int ino;
  char *name;

  /* The file serial number, which distringuishes this file from
     all other files on the same device.

     Hash the filename to hopefully make a fairly unique number.  */

  /*name = getFileName(__uname (fname, 0)); */

  r[0] = 37;
  r[1] = (int)fname;
  r[2] = (int)tmp;
  r[3] = r[4] = 0;
  r[5] = 255;
  ino = r[3] + r[4];
  if (!myos_swi (OS_FSControl, r) && r[5] >= 0)
    {
      for (s = tmp; s[0] || s[1]; s += 2)
        ino += (s[1] == 0) ? s[0]<<5 : s[0] * s[1];
    }
  return ino;
}

static void __stat (register int *r, register struct stat *buf)
{
  buf->st_ino = __get_file_serial_no ((char *)r[1]);

  buf->st_mode = ((r[5] & 0001) << 8) | ((r[5] & 0002) << 6) |
    ((r[5] & 0020) >> 2) | ((r[5] & 0040) >> 4);

  switch (r[0])
    {
    case 1:
      buf->st_mode |= S_IFREG;
      break;
    case 2:			/* Normal directory */
      buf->st_mode |= S_IFDIR | 0700;	/* FS bug */
      break;
    case 3:			/* Image directory (RISC OS 3 and above) */
      if (expandZips==False && (((r[2] & 0xfff00) >> 8) == 0xddc)) 
      {
         buf->st_mode |= S_IFREG;
      }
      else
      {
         buf->st_mode |= S_IFDIR | ((buf->st_mode & 0400) >> 2);	/* FS bug */
      }
      break;
    }

  switch (major (buf->st_dev))
    {
    case DEV_TTY:
      buf->st_mode |= S_IFCHR;
      break;
    case DEV_PIPE:
      buf->st_mode |= 0;
      break;
    }

  /* The number of hard links to the file. For RISC OS, there is only
     one hard link since only one directory can point to the file.
     Symbolic links are not counted in the total.  */
  buf->st_nlink = 1;
  buf->st_uid = 1;
  buf->st_gid = 1;
  buf->st_rdev = 0;
  buf->st_size = r[4];
  /* Disc space that the file occupies, measured in units of 512
     byte blocks.  */
/*  buf->st_nblocks = r[4] / 512;  */
  /* Optimal block size for reading or writing this file, in bytes.
     Since sector sizes are usually 512 or 1024 bytes on RISC OS
     disc formats, we'll safely assume a block size of 1024.  */
  buf->st_blksize = 1024;

  if ((((unsigned int) r[2]) >> 20) == 0xfff)	/* date stamped file */
    {
      register unsigned int t1, t2, tc;

      t1 = (unsigned int) (r[3]);
      t2 = (unsigned int) (r[2] & 0xff);

      tc = 0x6e996a00U;
      if (t1 < tc)
	t2--;
      t1 -= tc;
      t2 -= 0x33;		/* 00:00:00 Jan. 1 1970 = 0x336e996a00 */

      t1 = (t1 / 100) + (t2 * 42949673U);	/* 0x100000000 / 100 = 42949672.96 */
      t1 -= (t2 / 25);		/* compensate for .04 error */

      buf->st_atime = buf->st_mtime = buf->st_ctime = t1;
    }
  else
    buf->st_atime = buf->st_mtime = buf->st_ctime = 0;
}
