#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);
}
