/*
@(SH) Funzioni per la gestione dei file a record.

@(M$) PRIVATE 

CalcPos(Rec,Len,Base) : Calcola la posizione del record nel file

@(C$) PRIVATE

LOCKSEM               : 'locksem' ; nome del semaforo utilizzato per il lock

@(VG$) PRIVATE

ld                    : struttura su disco che contien i dati per i lock sui file
sizeld                : numero di byte occupati dalla struttura ld
semres                : flag per l'ok delle operazioni sui semafori
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
*/

#include	"ccustio.h"         
#include "checks.h"
#ifdef DOS
#include	<sys/types.h>
#endif
#include	<sys/stat.h>
#ifndef DOS
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#else
#include <io.h>
#include <share.h>
#include <sys/locking.h>           
#include <dos.h>
#include "modaut.h"
#endif

#define		CalcPos(Rec, Len, Base) (((Rec) - 1) * ((RecNoType) (Len)) + ((RecNoType) (Base)))
#define LOCK_OFF 1200000000L

#ifndef DOS
	#define		LOCKSEM	"locksem"

	/* extern long lseek(int, long, int);*/
	extern int chsize(int, long);
	extern int lockf(int, int, long);
#endif

	unsigned	setlock(unsigned);
	int				seeklk(SecDef *, long, unsigned, unsigned, long);
#ifndef DOS
	int	semtran(char *);
	static void semcall(int, int);
	void	PS(int); 
	void	VS(int);
#endif

/*
BOOLEAN test_share()
{
#ifdef DOS
  static BOOLEAN share_active = 2;
  
  if (share_active == 2)
  {
    share_active = CGetAut(MUAUT);
    if (share_active)
    {
      int f = open("net.ini", O_RDONLY, SH_DENYNO, S_IREAD);
   
      share_active = f != -1;
      if (f != -1) close(f);    
    }
  }
  return share_active; 
#else
  return TRUE; 
#endif
}
*/


/*
@($) setlock  CUSTIO

@(ID)
Trasforma il modo di lock utente in modo lock XENIX.
@(FD)

@(ISV)
Nella versione XENIX :

lm = numero lock xenix.
@(FSV)

@(IN)
Restituisce il comando XENIX.
@(FN)
*/

	unsigned	setlock(LockMode)
	unsigned	LockMode; /* Tipo di lock */

		{

			int		lm;

			LockMode &= RecLockTypes ;
#ifdef DOS
			if (LockMode == ShareLock) lm = _LK_NBRLCK ;
			else
				if (LockMode == Lock) lm = _LK_NBRLCK ;
				else lm = _LK_UNLCK ;
#else
			if (LockMode == ShareLock) lm = F_TLOCK ;
			else
				if (LockMode == Lock) lm = F_TLOCK ;
				else lm = F_ULOCK ;
#endif
			return(lm) ;
		}

/*
@($) seeklk  CUSTIO

@(ID)
Effettua una seek.
@(FD)

@(ISV)
junk = variabile di lavoro.

lock = variabile per eseguire comandi di lock.
@(FSV)
*/

	int seeklk(S, pos, lm, LockMode, rec) 
        SecDef			*S ;      /* Descrittore file               */
	long 				pos ;     /* posizione all'interno del file */
	unsigned		lm ;      /* comando di lock per XENIX      */
	unsigned		LockMode; /* comando di lock utente         */ 
        long                    rec;      /* n.ro di record                 */

	{
  	  if (LockMode != NoLock && S->LockMode != ExclLock) 
	  {               
#ifdef DOS
/* 	  if (test_share())  */
	    { 
        if (_lseek(S->F, LOCK_OFF + rec, SEEK_SET) == -1L)
	        if ((S->IOR = CIOResult()) != NoErr) return(-1);
   	   
   	    if ( _locking(S->F,lm, 1L) == -1)
	      { 
          S->IOR = CIOResult();
                
          if (S->IOR == EACCES) S->IOR = EAGAIN;   
          if (LockMode == UnLock || LockMode == NoLock) S->IOR = NoErr;
	        if (S->IOR != NoErr) return(-1);
	      }  
  	  }
#else
	    if (lseek(S->F, LOCK_OFF + rec, SEEK_SET) == -1L)
	      if ((S->IOR = CIOResult()) != NoErr) return(-1) ;
	    if (lockf(S->F,lm, 1L) == -1)
		  if ((S->IOR = CIOResult()) != NoErr) return(-1) ;
#endif
	  }
#ifdef DOS
    	  if (_lseek(S->F, pos, SEEK_SET) == -1L)
#else
    	  if (lseek(S->F, pos, SEEK_SET) == -1L)
#endif
	    if ((S->IOR = CIOResult()) != NoErr) return(-1) ;
	  return(0) ;
	}

/*
@(#) CLockRec  CUSTIO

@(ID)
Esegue un comando lock sul record nro. "rec" del file descritto da "s".
@(FD)

@(ISV)
Solo versione XENIX.
@(FSV)
*/

	void CLockRec(s, rec, lockmode) 
	SecDef			*s ; /* descrittore del file */
	RecNoType 	rec ; /* numero record su cui effettuare il lock */
	unsigned		lockmode;/* operazione lock da effettuare  */

	{
	  seeklk(s, CalcPos(rec, s->LenRec, s->BaseFil), setlock(lockmode), lockmode, rec);
	}

/*
@(#) CVerify  CUSTIO

@(ID)
Verifica che il file "name" esista.
@(FD)

@(ISV)
junk = variabile di lavoro.
@(FSV)
*/

	void				 CVerify(S, Name)
	SecDef				*S;   /* descrittore File */
	FileName			Name; /* nome file        */

		{
			int	junk ;

			S->IOR = NoErr;
#ifdef DOS
			if ((S->F = sopen(Name, O_RDONLY | O_BINARY, SH_DENYNO, S_IREAD | S_IWRITE)) == -1)
#else
			if ((S->F = open(Name, O_RDONLY, 0666)) == -1)
#endif
				S->IOR = CIOResult();
			else
				if (close(S->F) == -1)
				{
					junk = CIOResult();
					if (!S->IOR) S->IOR = junk;
				}
		}

/*
@(#) COpen  CUSTIO

@(ID)
Apre un file di record.
@(FD)

@(ISV)
junk = variabile di lavoro.
@(FSV)
*/

	void				 COpen(S, Name, Len, Base, LockMode)
	SecDef	*S;            /* descrittore file */
	FileName			Name;    /* nome del file    */
	unsigned			Len;     /* lunghezza record */
	unsigned			Base;    /* offset dall'inizio del file fisico; e' sempre 0 per i file dati */
	unsigned			LockMode;/* lock di apertura del file    */

		{
			int	junk;

			S->IOR = NoErr;
#ifdef DOS
			if ((S->F = sopen(Name, O_RDWR|O_BINARY,
					/*  test_share() && */ LockMode == ExclLock ? SH_DENYRW : 
					  SH_DENYNO, S_IREAD|S_IWRITE)) == -1)
#else
			if ((S->F = open(Name, O_RDWR , 0666)) == -1)
#endif
				if ((S->IOR = CIOResult()) != NoErr) return ;
			S->LenRec = Len;
			S->BaseFil = Base * BlockLenIO ;
			S->LockMode = LockMode ;
			S->lpos = -1;
			strcpy(S->name, Name);
			if (lseek(S->F, 0L, SEEK_SET) == -1L)
				if ((S->IOR = CIOResult()) != NoErr) return ;
			if (excllock(Name, (S->LockMode == ExclLock)) == -1)
			{
				S->IOR = CIOResult();
				if (close(S->F) == -1) junk = CIOResult();
			}
		}
/*
@(#) CCreate  CUSTIO

@(ID)
Crea un nuovo file di record.
@(FD)

@(ISV)
junk = variabile di lavoro.
@(FSV)
*/

	void				CCreate(S, Name, Len, Base, MaxSec)
	SecDef				*S;     /* descrittore del file */
	FileName			Name;   /* nome del file        */
	unsigned			Len;    /* lunghezza del record */
	unsigned			Base;   /* offset dall'inizio del file fisico  */
	RecNoType			MaxSec; /* Numero di blocchi di disco da allocare */

		{
			int				junk;

			S->IOR = NoErr;
#ifdef DOS
			if ((S->F = sopen(Name, O_RDWR | O_CREAT, SH_DENYNO, S_IREAD | S_IWRITE)) == -1)
#else
			if ((S->F = open(Name, O_RDWR | O_CREAT, 0666)) == -1)
#endif
				if ((S->IOR = CIOResult()) != NoErr) return ;
			if (chsize(S->F, ((RecNoType) MaxSec)*BlockLenIO) == -1)
				S->IOR = CIOResult();
			if (close(S->F) == -1)
			{
				junk = CIOResult();
				if (!S->IOR) S->IOR = junk;
			}
		}

/*
@(#) CChsize  CUSTIO

@(ID)
Cambia la dimensione di un file di record.
@(FD)

@(ISV)
junk = variabile di lavoro.
@(FSV)
*/

	void				CChsize(S, Name, Len, Base, MaxSec)
	SecDef				*S;     /* descrittore del file */
	FileName			Name;   /* nome del file        */
	unsigned			Len;    /* lunghezza del record */
	unsigned			Base;   /* offset dall'inizio del file fisico */ 
	RecNoType			MaxSec; /* numero di blocchi del file modificati */

		{
			int				junk;

			S->IOR = NoErr;
#ifdef DOS
			if ((S->F = sopen(Name, O_RDWR | O_BINARY, SH_DENYNO, S_IREAD | S_IWRITE)) == -1)
#else
			if ((S->F = open(Name, O_RDWR, 0666)) == -1)
#endif
				if ((S->IOR = CIOResult()) != NoErr) return ;
			if (chsize(S->F, ((RecNoType) MaxSec)*BlockLenIO) == -1)
				S->IOR = CIOResult();
			if (close(S->F) == -1)
			{
				junk = CIOResult();
				if (!S->IOR) S->IOR = junk;
			}
		}

/*
@(#) CClose  CUSTIO

@(ID)
Chiude un file di record. 
@(FD)

*/

	void				 CClose(S)
	SecDef	*S; /* descrittore del file da chiudere */

		{
			S->IOR = NoErr;
#ifdef DOS
			if (close(S->F) == -1) S->IOR = CIOResult();
#else
			if (S->LockMode == AutoLock)
			{
				if (S->lpos != -1)
				{
					if (lseek(S->F, S->lpos, SEEK_SET) == -1L)
						if ((S->IOR = CIOResult()) != NoErr) return ;
					if (lockf(S->F,F_ULOCK,(long) S->LenRec) == -1)
						if ((S->IOR = CIOResult()) != NoErr) return ;
				}
			}
			if (close(S->F) == -1) S->IOR = CIOResult();
			if (exclunlock(S->name, (S->LockMode == ExclLock)) == -1)
				S->IOR = CIOResult();
#endif
		}

/*
@(#) CDelete  CUSTIO

@(ID)
Cancella un file di record.
@(FD)
*/


	void				 CDelete(S, Name)
	SecDef	*S;         /* descrittore del file */
	FileName			Name; /* Nome del file        */

		{
			if (unlink(Name) == -1)
				S->IOR = CIOResult();
			else
				S->IOR = NoErr;
		}

/*
@(#) CRead  CUSTIO

@(ID)
Legge un record del file.
@(FD)

@(ISV)
junk = variabile di lavoro.

fpos = contiene la posizione del record all'interno del file.
@(FSV)

@(IN)
Lo spazio necessario a contenere il record letto deve essere stato allocato in precedenza.
@(FN)
*/

	void				 CRead(S, RecBuf, Rec, LockMode)
	SecDef	*S;             /* descrittore del file           */
	RecType				RecBuf;   /* spazio per contenere il record */
	RecNoType			Rec;      /* Record da leggere              */
	unsigned			LockMode; /* lock sul record                */

		{
			int				junk;
			register	RecNoType	fpos;

			if (Rec)
			{
				S->IOR = NoErr;
				fpos = CalcPos(Rec, S->LenRec, S->BaseFil);
#ifndef DOS
				if (S->LockMode == AutoLock)
				{
					if (S->lpos != -1)
						if (seeklk(S, S->lpos, F_ULOCK, LockMode, (S->lpos - S->BaseFil) / S->LenRec) == -1) return ;
					if (seeklk(S, fpos, F_TLOCK, LockMode, Rec) == -1) return ;
					S->lpos = fpos ;
				}
				else
#endif
					if (seeklk(S, fpos, setlock(LockMode), LockMode, Rec) == -1) return ;
				if (read(S->F, RecBuf, (unsigned) S->LenRec) == -1)
				{
					S->IOR = CIOResult();
					junk = seeklk(S, fpos, setlock(UnLock), LockMode, Rec) == -1;
				}
			}
			else S->IOR = 3;
	 }

/*
@(#) CWrite  CUSTIO

@(ID)
Scrive un record nel file.
@(FD)

@(ISV)
junk = variabile di lavoro.

fpos = contiene la posizione del record all'interno del file.
@(FSV)

@(IN)
Lo spazio necessario a contenere il record letto deve essere stato allocato in precedenza.
@(FN)
*/

	void				 CWrite(S, RecBuf, Rec, LockMode)
	SecDef	*S;             /* descrittore del file                      */
	RecType				RecBuf;   /* spazio che contiene il record da scrivere */
	RecNoType			Rec;      /* numero del record da scrivere             */
	unsigned			LockMode; /* lock sul record                           */

		{
			int				junk;
			register	RecNoType	fpos;

			if (Rec)
			{
				S->IOR = NoErr;
				fpos = CalcPos(Rec, S->LenRec, S->BaseFil);
#ifndef DOS
				if (S->LockMode == AutoLock)
				{
					if (S->lpos != -1)
						if (seeklk(S, S->lpos, F_ULOCK, LockMode, (S->lpos - S->BaseFil) / S->LenRec) == -1) return ;
					if (seeklk(S, fpos, F_TLOCK, LockMode, Rec) == -1) return ;
					S->lpos = fpos ;
				}
				else
#endif
					if (seeklk(S, fpos, setlock(LockMode), LockMode, Rec) == -1) return ;
				if (write(S->F, RecBuf, (unsigned) S->LenRec) == -1)
				{
					S->IOR = CIOResult();
					junk = seeklk(S, fpos, setlock(UnLock), LockMode, Rec) == -1;
				}
			}
			else S->IOR = 3;
		}

/*
@(#) IDeleteRec  CUSTIO

@(ID)
Cancellazione logica di un record.
@(FD)
*/

	void				 IDeleteRec(RecBuf)
	RecType			 RecBuf; /* record da cancellare */

		{
			RecBuf[0] = Deleted;
		}

/*
@(#) IRecallRec  CUSTIO

@(ID)
Recupero logico di un record.
@(FD)
*/

	void				 IRecallRec(RecBuf)
	RecType			 RecBuf; /* record da recuperare logicamente */

		{
			RecBuf[0] = Valid;
		}

/*
@(#) IRecIsDeleted  CUSTIO

@(ID)
Controlla se il record in "RecBuf" e' cancellato oppure no.
@(FD)
*/

	BOOLEAN		IRecIsDeleted(RecBuf)
	RecType		RecBuf; /* record da controllare */

		{
			return (RecBuf[0] == Deleted);
		}

/*
@(#) ITestLock  CUSTIO

@(ID)
Controlla se l'errore "err" e' dovuto ad un lock.
@(FD)

@(ISV)
Versione DOS e XENIX.
@(FSV)
*/


BOOLEAN		ITestLock(err)
int		err;  /* codice di errore */

{
  return(TESTLOCK(err)) ;
}

#ifndef DOS
struct lockdata
			{
				int users;
				int excl;
			} ld;

int sizeld = sizeof(struct lockdata);

/*
@($) lockpath  CUSTIO

@(ID)
Restituisce il nome del file di lock per il file "name".
@(FD)

@(ISV)
path = percorso per il file (/usr/tmp/....).

s1,s,s2   = stringhe di lavoro (nomi file).

Solo versione XENIX.
@(FSV)
*/

char 		*lockpath(name)
char	*name; /* stringa nome file */

	{
		static char	path[200];
		char *s1 = name, *s = name, *s2 = name;

		while ((s1 = strchr(s, DIRSEP)) != NULL)
		{
			s2 = s;
			s = s1 + 1;
		}
		sprintf(path, "/usr/tmp/%s", s2);
		return(path);
	}

/*
@($) dirname  CUSTIO

@(ID)
Estrae dal path il nome del direttorio.
@(FD)

@(ISV)
path = percorso per il file .

s   = stringa di lavoro.

Solo versione XENIX.
@(FSV)
*/

char 		*dirname(name)
char	*name; /* stringa contenente il path */

	{
		static char	path[200];
		char					*s;

		strcpy(path,name);
		if ((s = strrchr(path, DIRSEP)) == NULL) strcpy(path, ".");
		else *s = '\0';
		return(path);
	}

static int	semres = 0;

/*
@($) semtran  CUSTIO

@(ID)
Trasforma il nome di un semaforo in un identificatore poi lo crea o lo apre.
@(FD)

@(ISV)
key = chiave corrispondente al nome semaforo.

id  = identificatore del semaforo.

Solo versione XENIX.
La semget fa una open sul semaforo; se non lo trova lo crea e poi lo libera.
@(FSV)
*/

int	semtran(s)
char	*s; /* stringa nome del semaforo */

	{
		register	int key = 0, sid;

		semres = 0 ;
		while (*s) key += *s++;
		if ((sid = semget((key_t) key, 1, 0666)) == -1)
		{
			if ((sid = semget((key_t) key, 1, 0666 | IPC_CREAT)) == -1)
				semres = -1;
			VS(sid);
		}
		return(sid);
	}

/*
@($) semcall  CUSTIO

@(ID)
Chiama il semaforo "sid" e su di esso svolge "op".
@(FD)

@(ISV)
sb = struttura necessaria per la chiamata a "semop".

Solo versione XENIX.
@(FSV)

@(IN)
se op = -1 il semaforo viene occupato.

se op = +1 il semaforo viene liberato.
@(FN)
*/

static void semcall(sid, op)
int		sid; /* identificatore semaforo */
int		op;  /* operazione da effettuare sul semaforo: libero/occupato */

	{
		struct sembuf	sb;

		semres = 0 ;
		sb.sem_num = 0;
		sb.sem_op = op;
		sb.sem_flg = 0;
		if (semop(sid, &sb, 1) == -1)
			semres = -1;
	}

/*
@($) PS  CUSTIO

@(ID)
Acquisisce il semaforo "sid".
@(FD)

@(ISV)
Solo versione XENIX.
@(FSV)
*/

void	PS(sid)
int	sid; /* identificatore semaforo */

	{
		semcall(sid, -1);
	}

/*
@($) VS  CUSTIO

@(ID)
Rilascia il semaforo "sid".
@(FD)

@(ISV)
Solo versione XENIX.
@(FSV)
*/

void	VS(sid)
int	sid;

	{
		semcall(sid, 1);
	}
#endif

/*
@(#) excllock  CUSTIO

@(ID)
Funzione per la gestione di un lock esclusivo su tutto il file.
@(FD)

@(ISV)
Nella versione XENIX:

path = contiene il file di lock.

dir  = contiene la directory.

fd        = identificatore restituito dalla "open".

junk = variabile di lavoro.

sid       = identificatore del semaforo su locksem.

errno     = numero errore.

sb        = struttura che contiene informazioni sullo stato del file (vedi "stat").

@(FSV)
*/

int excllock(name,excl)
char	*name;  /* nome del file                               */
BOOLEAN excl; /* se true deve fare il lock ex. altrimenti no */

	{
#ifndef DOS
		char	path[200], dir[200];
		int		fd, junk, sid;
		extern int errno;
		struct stat	sb;

		strcpy(path, lockpath(name));
		strcpy(dir, dirname(path));
		sid = semtran(LOCKSEM);
		if (semres == -1) return(-1);
		PS(sid);
		if (semres == -1) return(-1);
		if ((stat(dir, &sb) == -1) && (errno == ENOENT)) CMkDir(dir);
		if (((fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0666)) == -1) && 
						errno == EEXIST)
		{
			errno = 0;
			if ((fd = open(path, O_RDWR, 0666)) == -1)
			{
				VS(sid);
				return(-1);
			}
			if ((read(fd, &ld, sizeld) <= 0) ||
					(lseek(fd, 0L, SEEK_SET) == -1L))
			{
				junk = close(fd);
				VS(sid);
				return(-1) ;
			}
			if (((ld.excl) && (ld.excl != getpid())) || excl)
			{
				junk = close(fd);
				errno = EACCES ;
				VS(sid);
				return(-1);
			}
			ld.users++;
		}
		else
		{
			if (fd == -1)
			{
				VS(sid);
				return(-1);
			}
			errno = 0;
			ld.users = 1;
			if (excl) ld.excl = getpid();
			else ld.excl = 0;
		}
		if (write(fd, &ld, sizeld) == -1) 
		{
			junk = close(fd);
			VS(sid);
			return(-1);
		}
		if (close(fd) == -1)
		{
			VS(sid);
			return(-1);
		}
		VS(sid);
		if (semres == -1) return(-1);
#endif
		return(0);
	}

/*
@(#) exclunlock  CUSTIO

@(ID)
Gestisce un unlock esclusivo su tutto il file.
@(FD)

@(ISV)
Nella versione XENIX:

path = contiene il file di lock.

fd        = identificatore restituito dalla "open".

junk = variabile di lavoro.

sid       = identificatore del semaforo su locksem.
@(FSV)
*/

int	exclunlock(name,excl)
char	*name;  /* nome file */
BOOLEAN excl; /* se true esegue un unlock sul file */

	{
#ifndef DOS
		int				fd, junk, sid;
		char 			path[200];

		strcpy(path, lockpath(name));
		sid = semtran(LOCKSEM);
		if (semres == -1) return(-1);
		PS(sid);
		if (semres == -1) return(-1);
		if (((fd = open(path, O_RDWR , 0666)) == -1) ||
				(read(fd, &ld, sizeld) <= 0) ||
				(lseek(fd, 0L, SEEK_SET) == -1L))
		{
			junk = close(fd);
			VS(sid);
			return(-1);
		}
		if (excl) ld.excl = 0;
		if (--ld.users == 0)
		{
			if ((close(fd) == -1) || (unlink(path) == -1))
			{
				VS(sid);
				return(-1);
			}
			VS(sid);
			if (semres == -1) return(-1);
			return(0);
		}
		if (write(fd, &ld, sizeld) == -1)
		{
			junk = close(fd);
			VS(sid);
			return(-1);
		}
		if (close(fd) == -1)
		{
			VS(sid);
			return(-1);
		}
		VS(sid);
		if (semres == -1) return(-1);
#endif
		return(0);
	}