/* Software License Agreement
 * 
 *     Copyright(C) 1994-2020 David Lindauer, (LADSoft)
 * 
 *     This file is part of the Orange C Compiler package.
 * 
 *     The Orange C Compiler package 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 3 of the License, or
 *     (at your option) any later version.
 * 
 *     The Orange C Compiler package 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 Orange C.  If not, see <http://www.gnu.org/licenses/>.
 * 
 *     As a special exception, if other files instantiate templates or
 *     use macros or inline functions from this file, or you compile
 *     this file and link it with other works to produce a work based
 *     on this file, this file does not by itself cause the resulting
 *     work to be covered by the GNU General Public License. However
 *     the source code for this file must still be made available in
 *     accordance with section (3) of the GNU General Public License.
 *     
 *     This exception does not invalidate any other reasons why a work
 *     based on this file might be covered by the GNU General Public
 *     License.
 * 
 *     contact information:
 *         email: TouchStone222@runbox.com <David Lindauer>
 * 
 */

#include <io.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <wchar.h>
#include <locale.h>
#include "libp.h"
#include <assert.h>
#include <fcntl.h>
#include <stdarg.h>
#include <errno.h>

extern int __uihandles[HANDLE_MAX], __uimodes[HANDLE_MAX], __uiflags[HANDLE_MAX] ;
extern char __uinames[HANDLE_MAX][256] ;
extern struct flock *__uilocks[HANDLE_MAX];

static int fcntl__uinewhandpos(int lowest)
{
   int i;
   for (i=lowest; i < HANDLE_MAX; i++)
      if (__uihandles[i] == -1)
         return i ;
   errno = EMFILE ;
   return -1 ;
}
static int _RTL_FUNC fcntl_dup(int __handle, int lowest)
{
   int h ; 
   int i,ohand = __handle ;
   __ll_enter_critical() ;
   h = fcntl__uinewhandpos(lowest) ;
   if (h == -1) {
      __ll_exit_critical() ;
      return h ;
   }
   __handle = __uiohandle(__handle) ;
   if (__handle == -1) {
	      __ll_exit_critical() ;
      return __handle ;
   }
   i = __ll_dup(__handle) ;
   if (i == 0) {
      __ll_exit_critical() ;
      return -1 ;
   }
   __uimodes[h] = __uimodes[ohand] & ~FD_CLOEXEC ;
   __uihandles[h] = i ;
   __uiflags[h] = __uiflags[ohand] ;
   strcpy(__uinames[h],__uinames[ohand]) ;
   __ll_exit_critical() ;
   return h ;
}
static int fcntl_getlock(int __handle, struct flock *lock)
{
       int istart = lock->l_len < 0 ? lock->l_start - lock->l_len : lock->l_start;
       int iend = lock->l_len < 0 ? lock->l_start : lock->l_start + lock->l_len; 
    struct flock *locks = __uilocks[__handle];
    while (locks)
    {
       int start = locks->l_len < 0 ? locks->l_start - locks->l_len : locks->l_start;
       int end = locks->l_len < 0 ? locks->l_start : locks->l_start + locks->l_len; 
       if (start <= istart && end > istart || start < iend && end > istart)
       {
           *lock = *locks;
           return 0;
       }
       locks = locks->l_next;
    }
    lock->l_type = F_UNLCK;
// pid not supported
    lock->l_pid = 0;
    return 0;
}
static int fcntl_getpos(int __handle, int whence)
{
    if (whence == SEEK_SET)
    {
       return 0;
    }
    else if (whence == SEEK_CUR)
    {
        return __ll_getpos(__uihandles[__handle]);
    }
    else
    {
        int n = __ll_getpos(__uihandles[__handle]);
        if (n == -1 )
            return n;
        if (__ll_seek(__uihandles[__handle],0, SEEK_END) == -1)
            return -1;
        int rv = __ll_getpos(__uihandles[__handle]);
        if (rv == -1)
            return rv;
        if (__ll_seek(__uihandles[__handle],0, SEEK_SET) == -1)
            return -1;
        return rv;
    }
}
static int fcntl_setlock(int __handle, struct flock *lock, int wait)
{
    if (lock->l_type == F_UNLCK)
    {
        struct flock ilock = *lock;
        int pos = fcntl_getpos(__handle, lock->l_whence);
        ilock.l_start += pos;
        ilock.l_whence = SEEK_SET;
        int start = ilock.l_len < 0 ? ilock.l_start - ilock.l_len : ilock.l_start;
        int end = ilock.l_len < 0 ? ilock.l_start : ilock.l_start + ilock.l_len; 
        struct flock **search = &__uilocks[__handle];
        while (*search)
        {
            int sstart = (*search)->l_len < 0 ? (*search)->l_start - (*search)->l_len : (*search)->l_start;
            int send = (*search)->l_len < 0 ? (*search)->l_start : (*search)->l_start + (*search)->l_len;
            if (start == sstart && end == send)
                break;
            search = &(*search)->l_next;
        }	
        if (*search)
        {
            struct flock *p = *search;
            *search = (*search)->l_next;
            free(p);
            return __ll_unlock(__uihandles[__handle], start, end);               
        }
        return -1;

    }
    struct flock *ilock = calloc(1, sizeof(struct flock));
    if (!ilock)
    {
        errno = ENOMEM;
        return -1;
    }
    *ilock = *lock;
    int pos = fcntl_getpos(__handle, lock->l_whence);
    ilock->l_start += pos;
    ilock->l_whence = SEEK_SET;
    struct flock **search = &__uilocks[__handle];
    int start = ilock->l_len < 0 ? ilock->l_start - ilock->l_len : ilock->l_start;
    int end = ilock->l_len < 0 ? ilock->l_start : ilock->l_start + ilock->l_len; 
    while (*search)
    {
       int sstart = (*search)->l_len < 0 ? (*search)->l_start - (*search)->l_len : (*search)->l_start;
       if (sstart > start)
           break;
       search = &(*search)->l_next;
    }
    ilock->l_next = *search;
    *search = ilock;
    return __ll_lock(__uihandles[__handle], start, end, lock->l_type == F_WRLCK, wait);
}
int _RTL_FUNC fcntl(int __handle, int type, ...)
{
   int rv = 0;
   va_list args;
   va_start(args, type);
   int mode ;
   __ll_enter_critical() ;
   switch (type)
   {
       case F_DUPFD:
           rv = fcntl_dup(__handle, va_arg(args, int));
           break;
       case F_GETFD:
	    rv = __uimodes[__handle] & FD_CLOEXEC;
            break;
       case F_SETFD:
            __uimodes[__handle] &= ~FD_CLOEXEC;
            __uimodes[__handle] |= va_arg(args,int) & FD_CLOEXEC;
            break;
       case F_GETFL:
	        rv = __uimodes[__handle];
            break;
       case F_SETFL:
	        __uimodes[__handle] &= 0xf00 | O_ACCMODE;
            __uimodes[__handle] |= ~(0xf00 | O_ACCMODE) & va_arg(args, int);
            break;
       case F_GETOWN:
       case F_SETOWN:
            // noop 
            break;
       case F_GETLK:
            rv = fcntl_getlock(__handle, va_arg(args, struct flock *));
            break;
       case F_SETLK:
            rv = fcntl_setlock(__handle, va_arg(args, struct flock *), 0);
            break;
       case F_SETLKW:
            rv = fcntl_setlock(__handle, va_arg(args, struct flock *), 1);
            break;
   }
   __ll_exit_critical() ;
   va_end(args);
   return rv ;
}
