/* Various version of an flock() for locking an open file

Modified by chrisp:
1) changed flock to myflock, needs changing in all source
2) added function int myflock_set_fcntl(int x), call to enable fcntl locking
   on a system that uses flock. 
3) All config.* files should choose one of:
	LOCK_NT, LOCK_FCNTL, LOCK_LOCKF, LOCK_FLOCK
   The default is LOCK_FLOCK for unix
   The default is LOCK_NT for NT
   Generally LOCK_FCNTL is a good choice for unix 

   If LOCK_FLOCK is used, then if NO_FCNTL is NOT defined then you can use
   an ini setting to switch to FCNTL locking by calling myflock_set_fcntl(TRUE);

   The ini setting is:
	LOCK_FCNTL TRUE
	
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define DMID 14
#include "debug.h"

#include "flock.h"

#ifdef WIN32
#include <errno.h>
#include <io.h>
#include <sys/locking.h>
#endif


#if defined(LOCK_FCNTL) || defined(LOCK_LOCKF)

#include <unistd.h>
#include <sys/types.h>
#include <errno.h>

#ifdef ISC
# include <net/errno.h>
#endif

#endif

/*	Call this based on ini file setting, this is sensible if
	your nfs system supports real fcntl locks and some do, others will just
	fail if you try this so it must be an optional setting.
*/
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif

static int use_fcntl=FALSE;
int myflock_set_fcntl(int x)
{
	use_fcntl = x;	
#ifdef NO_FCNTL
	if (x) return FALSE;
#endif
	return TRUE;
}

#if defined(LOCK_FLOCK)
#ifdef NO_FCNTL
static char *lock_style = "flock_only";
#else
static char *lock_style = "flock_or_fcntl";
#endif

#ifndef NO_FCNTL
int myflock_fcntl(int fd, int operation)
{
	int op, ret;
	struct flock arg;
	extern int errno;

	op = (LOCK_NB & operation) ? F_SETLK : F_SETLKW;
	
	arg.l_type = (LOCK_EX & operation) ? F_WRLCK :
		(LOCK_SH & operation) ? F_RDLCK : F_UNLCK;
	arg.l_whence = 0;
	arg.l_start = 0;
	arg.l_len = 0;
	arg.l_pid = 0;
	
	if ((ret = fcntl(fd, op, &arg)) == -1) {
		if (errno == EACCES || errno == EAGAIN)
			errno = EWOULDBLOCK;
	}
	return (ret);
}
#endif
int myflock(int fd, int operation)
{
#ifndef NO_FCNTL
	if (use_fcntl) return myflock_fcntl(fd,operation);
#endif
	return flock(fd,operation);
}
#elif defined(LOCK_FCNTL)
static char *lock_style = "flock_fcntl";
int myflock(int fd, int operation){
	int op, ret;
	struct flock arg;
	extern int errno;

/*	emsg("flock_fcntl locking (%d) with (%d)"); */
	op = (LOCK_NB & operation) ? F_SETLK : F_SETLKW;
	
	arg.l_type = (LOCK_EX & operation) ? F_WRLCK :
		(LOCK_SH & operation) ? F_RDLCK : F_UNLCK;
	arg.l_whence = 0;
	arg.l_start = 0;
	arg.l_len = 0;
	arg.l_pid = 0;
	
	if ((ret = fcntl(fd, op, &arg)) == -1) {
		if (errno == EACCES || errno == EAGAIN)
			errno = EWOULDBLOCK;
	}
	return (ret);
}
#elif defined(LOCK_FAKE)
static char *lock_style = "fake";
int myflock(int fd, int operation){
	return 1;
}
#elif defined(LOCK_LOCKF)
static char *lock_style = "lockf";
int myflock(int fd, int operation){
	int op, ret;
	extern int errno;

	op = (LOCK_UN & operation) ? F_ULOCK :
	  (LOCK_NB & operation) ? F_TLOCK : F_LOCK;

	if ((ret = lockf(fd, op, 0)) == -1) {
		if (errno == EACCES || errno == EAGAIN)
			errno = EWOULDBLOCK;
	}

	return (ret);
}
#elif defined(LOCK_NT)
static char *lock_style = "NT";
int myflock(int fd, int operation){
	int op=0,ret;
	if (operation & LOCK_UN) op |= LK_UNLCK;
	if (operation & LOCK_NB) op |= _LK_NBLCK;

	ret =_locking(fd,op,99999999L);
	if (errno==EACCES && (operation & LOCK_UN)) ret = 0;
	return ret;
}
#endif

char *myflock_style(void)
{
	if (strcmp(lock_style,"flock_or_fcntl")==0) {
		if (use_fcntl) return "flock_fcntl_enabled";
		else return "flock_fcntl_disabled";
	}
	return lock_style;
}
