/*
 *  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"

off_t
lseek (int fd, off_t off, int dir)
{
  struct file *f = u.u_ofile[fd];
  int res;

  /* if this is an open fd */
  if (fd >= 0 && fd < NOFILE && f)
    {
      if (f->f_type == DTYPE_FILE)
	{
          if (HANDLER_NIL(f))
	    {
	      errno = EINVAL;
	      return -1;
	    }

	  /* this will act differently than on Unix!! On the amiga,
	   * you CAN'T seek past the eof, you have to write something
	   * to extend a file. In 2.0 this will be easier with a
	   * "SetFileSize()" function */
	  __wait_packet(&f->f_sp);
	  /* reset the error field */
	  LastError(f) = 0;
	  SendPacket3(f,__rwport,ACTION_SEEK,f->f_fh->fh_Arg1,off,dir-1);
	  __wait_packet(&f->f_sp);
	  
	  if (LastError(f) == ERROR_ACTION_NOT_KNOWN || LastResult(f) < 0)
	    {
	      errno = __ioerr_to_errno(LastError(f));
	      res = -1;
	    }
	  else
	    {
	      errno = 0;

	      /* there's a subtle difference between Unix lseek() and AmigaDOS
	       * Seek(): lseek() returns the *current* position of the `pointer',
	       * Seek() returns the *previous* position. So in some cases, we
	       * have to do another Seek() to find out where we are.
	       * Thanks Mike for pointing me at this! */
	      if (!(dir == SEEK_CUR && off == 0))
		{
		  /* in that case, we do another Seek. We could try to calculate
		   * from the old pointer what the new pointer has to be, but this
		   * might be not what it really is, if we seeked past the
		   * file bounderies */
		  SendPacket3(f,__rwport,ACTION_SEEK,f->f_fh->fh_Arg1,0,0);
		  __wait_packet (&f->f_sp);
		}
	      res = LastResult(f);
	    }
	  
	  LastResult(f) = 0;
	  return res;
	}
      else if (f->f_type == DTYPE_MEM)
	{
	  int real_off;
	  int old_off = f->f_mf.mf_offset;
	  
	  real_off = (dir == L_SET ? off : 
		      (dir == L_INCR ? 
		       old_off + off : f->f_stb.st_size + off));
	  if (real_off < 0) real_off = 0;
	  else if (real_off > f->f_stb.st_size) real_off = f->f_stb.st_size;
	  f->f_mf.mf_offset = real_off;
	  return old_off;
	}
      else
	errno = ESPIPE;
    }
  else
    errno = EBADF;

  return -1;
}
