/*
_____       _    _    Corso   Italia,  178
(_|__   .  (_   |_|_  56125           Pisa
(_|_) |)|(()_)()| |   tel.  +39  050 46380
  |   |               picosoft@picosoft.it

 Copyright (C) Picosoft s.r.l. 1995-2002

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.

 This program 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 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
# include "wlockmgr.h"
# include <windows.h>
# include <io.h>
# include <errno.h>
# include <sys/locking.h>

# include <stdio.h>
# include <process.h>
# include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int
win_fcntl_lk (int fd, int cmd, struct flock *fl)
{
   static BOOL winNt = TRUE;
   long win32_start;
   long win32_len;
   DWORD startpos;
   HANDLE fdHndl = (HANDLE)_get_osfhandle(fd);
   BOOL rc;

   struct stat buf;
   errno = 0;
   fstat(fd, &buf);
   if (cmd == F_GETLK) {
      errno = ENOSYS;
      return -1;
   }

   switch (fl->l_whence) {
   case SEEK_SET:
      startpos = 0;
      break;
   case SEEK_CUR:
      if ((startpos = lseek (fd, 0, SEEK_CUR)) < 0)
         return -1;
      break;
   case SEEK_END:
      {
         BY_HANDLE_FILE_INFORMATION finfo;
         if (GetFileInformationByHandle (fdHndl, &finfo) == 0) {
            errno = GetLastError();
            return -1;
         }
         startpos = finfo.nFileSizeLow;
      }
      break;
   default:
      errno = EINVAL;
      return -1;
   }

   win32_start = startpos + fl->l_start;
   if (fl->l_len < 0) {
      win32_start -= fl->l_len;
      win32_len = -fl->l_len;
   } else
      win32_len = fl->l_len;

   if (win32_start < 0) {
      win32_len -= -win32_start;
      if (win32_len <= 0) {
         errno = EINVAL;
         return -1;
      }
      win32_start = 0;
   }

   if (win32_len == 0)
      win32_len = 0xffffffff;
   if (winNt) {
      OVERLAPPED ov;
      DWORD lock_flags = (cmd == F_SETLK) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
      lock_flags |= (fl->l_type == F_WRLCK) ? LOCKFILE_EXCLUSIVE_LOCK : 0;

      ov.Internal = 0;
      ov.InternalHigh = 0;
      ov.Offset = (DWORD)win32_start;
      ov.OffsetHigh = 0;
      ov.hEvent = (HANDLE) 0;

      if (fl->l_type == F_UNLCK) {
         rc = UnlockFileEx (fdHndl, 0, (DWORD)win32_len, 0, &ov);
      } else {
         rc = LockFileEx (fdHndl, lock_flags, 0, (DWORD)win32_len, 0, &ov);
         if (!rc && (lock_flags & LOCKFILE_FAIL_IMMEDIATELY) &&
             (GetLastError () == ERROR_LOCK_FAILED)) {
            errno = EAGAIN;
            return -1;
         }
      } 
      if (rc == 0) {
         errno = GetLastError ();
         rc = -1;
      } else
         rc = 0;
   } else {
      lseek (fd, fl->l_start, fl->l_whence);
      if (fl->l_type == F_UNLCK)
         rc = _locking (fd, _LK_UNLCK, win32_len);
      else if (cmd == F_SETLK)
         rc = _locking(fd, _LK_NBLCK, win32_len);
      else
         while ((rc = _locking(fd,_LK_LOCK,win32_len)) && errno == EDEADLOCK)
            errno = 0;
   }
   return rc;
}

