/******************************************************************************
 *
 * RCS ID
 * $Id: osfio,v 1.6 2002/11/17 16:43:22 david Exp $
 *
 * HISTORY
 * $Log: osfio,v $
 * Revision 1.6  2002/11/17 16:43:22  david
 * Updates to compiler with new 32-bit compiler
 *
 * Revision 1.5  2001/04/20 14:38:38  david
 * Updates for 0.7a
 * Corrected reporting of large disk sizes
 *
 * Revision 1.4  2000/01/29 17:21:24  david
 * Updated to try and fix a NT copying a read only file to RISCOS share bug
 *
 * Revision 1.3  1999/11/23 22:03:48  david
 * Updates file long filename support.
 * Increased filename length to MAXPATHLEN
 * Add stuff to treat an archive as a file and not a directory
 * if a flags is not set (useful for when !sparkfs laaded)
 * os_file - updated to be a C replacement for s._os
 *
 * Revision 1.2  1999/07/20 21:09:05  david
 * Added function to set filetype and calls to getFileName
 * to find the filename.
 *
 * Revision 1.1  1999/05/16 12:00:08  david
 * Initial revision
 *
 *
 *****************************************************************************/

#include "includes.h"
#include "kernel.h"
#include "my_os.h"
#include "swis.h"

#if !defined O_OMASK
#define O_OMASK 3
#endif

#define MAX_FILE_FDS     0x100
#define MAX_FILE_FD_MASK  0xFF

mode_t createModes[MAX_FILE_FDS];
int setModes[MAX_FILE_FDS];

int firstTime=1;

int init_riscos_fio(void)
{

  memset(createModes,0,MAX_FILE_FDS);
  memset(setModes,0,MAX_FILE_FDS);
  return 0;
}

int riscos_open(const char *pathname, int oflag, mode_t mode)
{
  int r[10];
  char *file,*file2;
  _kernel_oserror *e;
  int fd;
  int  newftype;
  int modifyMode=0;
  mode_t origMode=mode;


  DEBUG(5,("riscos_open %s, oflag %x, mode %o\n",
          pathname,oflag,mode));

  file2 = __uname ((char*)pathname, oflag & (O_CREAT | O_OMASK));

/*  if ( (oflag & O_CREAT)==O_CREAT)
  {
     file=getNewFileName(file,&newftype);
  }
  else
  {
     file=getFileName(file);
  }
*/
  /* get current file name */
  file=getFileName(file2);

  /* does file exist ? */
  if (e = myos_file (/*0x05*/ 20, file, r))
  {
    return (-1);
  }

  /* if r0 not 0 then file exists */
  if (r[0])
  {
    DEBUG(5,("risos_open %s exists\n",file));

    if ((oflag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))
    {
      errno = EEXIST;
      return (-1);
    }

    if (oflag & O_CREAT)
      oflag &= ~O_CREAT;

 /*   if (oflag & O_OMASK)
      if (e = myos_file (0x09, file, r))
      {
        return (-1);
      }
  */
    newftype=/*r[6]*/ (r[2] & 0xfff00)>>8;
  }
  else
  {
    /* else file doesn't exist */

    DEBUG(5,("risos_open %s does not exist\n",file));

    if ((oflag & (O_RDONLY | O_CREAT)) == O_RDONLY)
    {
      errno = ENOENT;
      return (-1);
    }
    if (oflag & O_OMASK)
      oflag |= O_CREAT;

    /* get new file name */
    file=getNewFileName(file2,&newftype);

    DEBUG(5,("risos_open new file name %s\n",file));
  }

  if (oflag & O_CREAT)
  {
    if (firstTime!=0)
    {
      firstTime=0;
      init_riscos_fio();
    }

    /* r[2] = (oflag & O_BINARY) ? 0xffd : 0xfff;*/
    /* TM: newtype limited to 12 Bit in every case */
    r[2]=newftype & 0x00000FFF;
    r[4] = r[5] = 0;
    if (e = myos_file (0x0b, file, r))
    {
      return (-1);
    }

     /* mode &= ~(__u->umask & 0777); */


    /*
     * If caller wants file to be writable or read/write make sure we don't
     * set it to read only br OR-ing in the write flag. The reason for this
     * hack is that when copying a read only file windows NT sends an
     * SMBNTCreateX that eventually calls this functions with oflag 0x202
     * and mode 0544 which is a contradiction! Linux copes with this!
     */
    if ( (oflag & O_WRONLY) || (oflag & O_RDWR) )
    {
      if ( (mode | 0200)== 0 )
      {
        mode |= 0200;
        DEBUG(5,("modifying mode %o\n",mode));
        modifyMode = 1;
      }
    }

    r[5] = ((mode & 0400) >> 8) | ((mode & 0200) >> 6) |
    ((mode & 0004) << 2) | ((mode & 0002) << 4);

    if (e = myos_file (0x04, file, r))
    {
      return (-1);
    }
  }

  if (e = myos_file (0x05, file, r))
  {
    return (-1);
  }

  if (oflag & O_TRUNC)
    e = myos_fopen (0x80, file, &fd);
  else
    switch (oflag & O_OMASK)
    {
      case O_RDONLY:
        e = myos_fopen (0x40, file, &fd);
        break;
      case O_WRONLY:
      case O_RDWR:
        e = myos_fopen (0xc0, file, &fd);
        break;
      default:
        errno = EINVAL;
        return (-1);
       break;
    }

  if (e)
  {
    DEBUG(5,("riscos_open failed %x,%s\n",e->errnum,e->errmess));
    return (-1);
  }

  if (modifyMode==1)
  {
    createModes[fd&MAX_FILE_FD_MASK] = origMode;
    setModes[fd&MAX_FILE_FD_MASK] = 1;
  }

  DEBUG(5,("riscos_open riscos-name %s, fd %d\n",file,fd));

  return (fd);
}

int riscos_close(int fd)
{
  _kernel_oserror *e;
  int r[10];
  _kernel_swi_regs rin, rout;
  mode_t mode;
  char *filename;


  DEBUG(5,("riscos_close fd %d\n", fd));

  if ( setModes[fd&MAX_FILE_FD_MASK] == 1)
  {
    /* need to find file name ! */
    /* 1st get file name size */
    rin.r[0]=7;
    rin.r[1]=fd;
    rin.r[2]=0;
    rin.r[5]=0;

    e = _kernel_swi(OS_Args, &rin, &rout);
    if (e==NULL)
    {
      /* get file name */
      filename = (char*)malloc(1-rout.r[5]);

      rin.r[0]=7;
      rin.r[1]=fd;
      rin.r[2]=(int)filename;
      rin.r[5]=1-rout.r[5];

      e = _kernel_swi(OS_Args, &rin, &rout);
      if (e!=NULL)
      {
        setModes[fd&MAX_FILE_FD_MASK] = 0;
        free(filename);
      }
    }
    else
    {
      setModes[fd&MAX_FILE_FD_MASK] = 0;
    }
  }

  if (e = myos_fclose (fd))
    {
      return (-1);
    }

  DEBUG(5,("riscos_close success\n"));

  if ( setModes[fd&MAX_FILE_FD_MASK] == 1)
  {

    DEBUG(5,("filename is %s\n",filename));

    mode = createModes[fd&MAX_FILE_FD_MASK];

    setModes[fd&MAX_FILE_FD_MASK] = 0;
    r[5] = ((mode & 0400) >> 8) | ((mode & 0200) >> 6) |
    ((mode & 0004) << 2) | ((mode & 0002) << 4);

    DEBUG(5,("setting mode to 0%o, r[5] %d\n",mode,r[5]));

    if (e = myos_file (0x04, filename, r))
    {
      DEBUG(5,("set attributes failed %x,%s\n",e->errnum,e->errmess));
    }
    free(filename);
  }

  return (0);
}

int riscos_read(int fd, void *buf, size_t count)
{
  int r[10];
  _kernel_oserror *e;


  if (e = myos_fread (fd, buf, count, r))
    {
      return (-1);
    }

  DEBUG(5,("riscos_read %d, number %d\n",fd, count-r[3]));

  return (count - r[3]);
}

int riscos_write(int fd, void *buf, size_t count)
{
  int r[10];
  _kernel_oserror *e;

  if (e = myos_fwrite (fd, buf, count, r))
    {
      return (-1);
    }

  DEBUG(5,("riscos_write %d, number %d\n",fd, count-r[3]));

  return (count - r[3]);

}

off_t riscos_lseek(int fd, off_t lpos, int whence)
{
  int r[10];
  _kernel_oserror *e;
  int report=0;


  switch (whence)
    {
    case 0:
      if (e = myos_args (0, fd, 0, r))
    {
      DEBUG(5,("riscos lseek failed case 0 - myos_args call 1\n"));
      return (-1);
    }
#if 0
      if ( r[2] < (int)lpos)
      {
        if (((int)lpos-r[2])>1000000)
          report = 1;
      }
      if (report==1)
      {
        DEBUG(0,("riscos_lseek %d, lpos %x, whence %d, current %d\n",
            fd, lpos, whence, r[2]));
      }
#endif
      if (e = myos_args (1, fd, (int) lpos, r))
    {
      DEBUG(5,("riscos lseek failed case 0: e %x,%s\n",e->errnum, e->errmess));
      return (-1);
    }
      break;
    case 1:
      if (e = myos_args (0, fd, 0, r))
    {
      DEBUG(5,("riscos lseek failed case 1 - myos_args call 1\n"));
      return (-1);
    }
      if (e = myos_args (1, fd, r[2] + (int) lpos, r))
    {
      DEBUG(5,("riscos lseek failed case 1 - myos_args call 2\n"));
      return (-1);
    }
      break;
    case 2:
      if (e = myos_args (2, fd, 0, r))
    {
      DEBUG(5,("riscos lseek failed case 2 - myos_args call 1\n"));
      return (-1);
    }
      if (e = myos_args (1, fd, r[2] + (int) lpos, r))
    {
      DEBUG(5,("riscos lseek failed case 2 - myos_args call 2\n"));
      return (-1);
    }
      break;
    default:
      DEBUG(5,("riscos lseek failed default case\n"));
      errno = EINVAL;
      return (-1);
      break;
    }

  if (report==1)
  {
    DEBUG(0,("riscos_lseek result %x\n",r[2]));
  }
  else
  {
    DEBUG(5,("riscos_lseek result %x\n",r[2]));
  }

  return ((long) r[2]);
}

int riscos_flush(int fd)
{
  int r[10];
  _kernel_oserror *e;

  DEBUG(6,("risos_flush %d\n",fd));

  if (e = myos_args (255, fd, 0, r))
    {
      return (-1);
    }

  DEBUG(6,("risos_flush success %d\n",fd));

  return 0;
}

int riscos_settype(char *fname, int ftype)
{
  int r[10];
  _kernel_oserror *e;

  r[0]=18;
  r[1]=(int)fname;
  r[2]=ftype;

  if (e = myos_swi (OS_File, r))
  {
    return (-1);
  }

  return 0;
}

