/***************************************************************************
 *                                                                         *
 *   DFAAPIP.C                                                             *
 *                                                                         *
 *   Copyright (c) 1987-1997 Galacticomm, Inc.   All Rights Reserved.      *
 *                                                                         *
 *   This file contains a library of routines for accessing BTRIEVE files  *
 *   with fixed or variable length records, from Protected-Mode DOS.       *
 *                                                                         *
 *                                            - T. Stryker 9/30/86         *
 *   Dup Ins/Upd, File Create, Misc others    - S. Brinker/R. Skurnick     *
 *   Step Based Btrieve Support Added         - R. Skurnick                *
 *   Windows DLL interface                    - T. Stryker                 *
 *   Locking/transaction support              - TJS/CLR/RLS 6/18/94        *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "protstuf.h"

#define FILREV "$Revision: 10 $"

INT _plmerror=0;                   /* Last Phar Lap Memory Error           */
USHORT dfalgrec=0;                 /* largest record size                  */

extern jmp_buf disaster;           /* error-recovery longjmp save block    */

static struct dfablk *dfa;         /* current btvu file pointer set        */
static struct dfablk *dfastk[DFSTSZ];/* dfaSetBlk()/dfaRstBlk() stack      */
static USHORT status;              /* status code returned by btvu()       */
static SHORT dfaomode=PRIMBV;      /* dfaOpen() file-open mode             */
static CHAR *gpbptr;               /* generic buffer pointer               */
static USHORT gpbseg;              /* real-mode segment of gpbptr          */
static USHORT *statusptr;          /* status code returned by btvu()       */
static USHORT statusseg;           /* real-mode segment of statusptr       */
static USHORT lastlen;             /* length of last record read           */
static USHORT dfadatseg;           /* real-mode segment of dfadat          */
#define ANOSEG  0x10               /* keyspc.flags for key has another seg */

static struct dfadat {             /* block structure used with INT 0x7B   */
     USHORT datbufoff;
     USHORT datbufseg;
     USHORT dbflen;
     USHORT posp38off;
     USHORT posp38seg;
     USHORT posblkoff;
     USHORT posblkseg;
     USHORT funcno;
     USHORT keyoff;
     USHORT keyseg;
     CHAR keylen;
     CHAR keyno;
     USHORT statptoff;
     USHORT statptseg;
     USHORT magic;
} *dfadatptr;

struct filspc {                    /* Btrieve STAT command file spec       */
     USHORT reclen;                /*   record length                      */
     USHORT pagsiz;                /*   page size                          */
     SHORT numofx;
     ULONG numofr;
     SHORT flags;
     SHORT reserved;
     SHORT unupag;
};

struct keyspc {                    /* Btrieve STAT command key specs       */
     SHORT keypos;
     SHORT keylen;
     SHORT flags;
     LONG numofk;
     CHAR extype;
     CHAR nullvalue;
     LONG reserved;
};

#define ACSSIZ 265                 /* alternate collating sequence size    */

struct statbf {                    /* STAT command ret buf layout          */
     struct filspc fs;
     struct keyspc ks[SEGMAX];
     CHAR altcol[ACSSIZ];
};

                                   /* internal file creation flags         */
#define DFACF_PREALLOC   4         /*   pre-allocate pages                 */
#define DFACF_USERVALID  (DFACF_VARIABLE|DFACF_BLANKTRUNC|DFACF_COMPRESS   \
                         |DFACF_KEYONLY|DFACF_FREESPACE1|DFACF_FREESPACE2)

                                   /* internal segment specification flags */
#define DFASF_SEGMENT    16        /*   another segment follows            */
#define DFASF_EXTENDED   256       /*   segment uses extended type         */
#define DFASF_USERVALID  (DFASF_ALTCOLLATE|DFASF_DESCENDING)
#define DFASF_KEYVALID   (DFAKF_DUPLICATE|DFAKF_MODIFYABLE|DFAKF_MANUAL|DFAKF_NULL)

struct dfaCreateFileSpec {         /* create internal file specification   */
     USHORT recordLength;          /*   record length                      */
     USHORT pageSize;              /*   page size                          */
     USHORT nKeys;                 /*   number of keys                     */
     CHAR notUsed[4];              /*   unused                             */
     USHORT flags;                 /*   Boolean options                    */
     CHAR reserved[2];             /*   reserved                           */
     USHORT nPreAllocate;          /*   number of pages to pre-allocate    */
};

struct dfaCreateKeySpec {          /* create internal key specification    */
     USHORT position;              /*   offset of key in data buffer       */
     USHORT length;                /*   length of key                      */
     USHORT flags;                 /*   Boolean options                    */
     CHAR notUsed[4];              /*   unused                             */
     UCHAR extendedType;           /*   extended type code                 */
     CHAR nullChar;                /*   character to use for NULL keys     */
     CHAR reserved[4];             /*   reserved                           */
};

static USHORT clckln(VOID);
static VOID dfaPosError(CHAR *recptr,const CHAR *stg);
static VOID dfaError(const CHAR *who);
static VOID eclint(USHORT inum,REGS16 *regs);
static GBOOL dfaCopyFile(const CHAR *src,const CHAR *dst);
static USHORT btvu(USHORT funcno,USHORT datbufseg,USHORT keyseg,
                   SHORT keyno,USHORT rlen);
static VOID maksur(USHORT size);

VOID (*dfaErrPtr)(const CHAR *who)=dfaError;

#define GPBP ((struct statbf *)gpbptr)
#define GIBP ((struct statbf *)gpbptr)

VOID
eclalcrem(                         /* allocate real-mode dynamic memory    */
USHORT size,                       /*   memory size to allocate            */
USHORT *seg,                       /*   segment to fill in after creating  */
CHAR **sel)                        /*   selector to fill-set after creating*/
{
     SEL pmsel;
     INT rv;

     DosBlockIntr();
     if ((rv=DosAllocRealSeg((ULONG)size,seg,&pmsel)) != 0) {
          DosUnblockIntr();
          catastro("REAL-MODE MEMORY POOL EXHAUSTED (rv=%d, %u bytes)",
                                                         rv,size);
     }
     DosUnblockIntr();
     *sel=MK_FP(pmsel,0);
}

VOID
maksur(                            /* ensure generic buffer is big enough  */
USHORT size)                       /*   new proposed maximum size          */
{
     static USHORT gpbsiz=0;

     if (size > gpbsiz) {
          eclalcrem(size,&gpbseg,&gpbptr);
          gpbsiz=size;
     }
}

DFAFILE *                          /*   pointer to a dfa file structure    */
dfaOpen(                           /* open data file                       */
const CHAR *filnam,                /*   filename                           */
USHORT maxlen,                     /*   maximum record length              */
const CHAR *owner)                 /*   owner of file (NULL if none)       */
{
     USHORT tseg;
     USHORT ownlen;
     static CHAR *ownbuf;
     static USHORT obfseg=0;

     if (obfseg == 0) {
          eclalcrem(16,&obfseg,&ownbuf);
     }
     if (maxlen > dfalgrec) {
          dfalgrec=maxlen;
     }
     if (statusptr == NULL) {
          eclalcrem(sizeof(USHORT),&statusseg,(char **)&statusptr);
          eclalcrem(sizeof(struct dfadat),&dfadatseg,(char **)&dfadatptr);
     }
     eclalcrem(sizeof(struct dfablk),&tseg,(char **)&dfa);
     dfa->realseg=tseg;
     dfa->filnam=alcdup(filnam);
     dfa->reclen=maxlen;
     maksur(dfa->reclen);
     dfa->data=alcmem(maxlen);
     maksur(strlen(filnam)+1);
     strcpy(gpbptr,filnam);
     if (owner == NULL) {
          setmem(ownbuf,16,0);
          ownlen=0;
     }
     else {
          stzcpy(ownbuf,owner,9);  /* HACK - 9? */
          ownlen=1+strlen(owner);
     }
     while (btvu(0,obfseg,gpbseg,dfaomode,ownlen) == 85) {
     }
     if (status != 0) {
          (*dfaErrPtr)("OPEN");
     }
     eclalcrem(clckln(),&dfa->keyseg,&dfa->key);
     dfaSetBlk(dfa);
     return(dfa);
}

VOID                               /*   Btrieve-specific function          */
dfaMode(                           /* set data file mode                   */
SHORT mode)                        /*   access mode                        */
{
     ASSERT(mode == PRIMBV
         || mode == ACCLBV
         || mode == RONLBV
         || mode == VERFBV
         || mode == EXCLBV);
     dfaomode=mode;
}

VOID
dfaSetBlk(                         /* set current dfablk to dfaptr         */
struct dfablk *dfaptr)             /*   datafile block pointer             */
{
     movmem(dfastk,dfastk+1,sizeof(struct dfablk *)*(DFSTSZ-1));
     *dfastk=dfa=dfaptr;
}

VOID
dfaRstBlk(VOID)                    /* restore last dfablk pointer          */
{
     movmem(dfastk+1,dfastk,sizeof(struct dfablk *)*(DFSTSZ-1));
     dfa=*dfastk;
}

VOID
dfaBegTrans(                       /* begin datafile transaction           */
USHORT loktyp)                     /*   lock bias                          */
{
     ASSERT(loktyp == WAITBV || loktyp == NOWTBV);
     if (btvu(19+loktyp,0,0,0,0) != 0) {
          (*dfaErrPtr)("BEGIN-XACTION");
     }
}

VOID
dfaAbtTrans(VOID)                  /* abort current datafile transaction   */
{
     if (btvu(21,0,0,0,0) != 0) {
          (*dfaErrPtr)("ABORT-XACTION");
     }
}

VOID
dfaEndTrans(VOID)                  /* end datafile transaction             */
{
     if (btvu(20,0,0,0,0) != 0) {
          (*dfaErrPtr)("END-XACTION");
     }
}

GBOOL                              /*   return TRUE=success                */
dfaQuery(                          /* datafile query operation             */
const VOID *key,                   /*   key to lookup                      */
SHORT keynum,                      /*   key number or -1                   */
USHORT qryopt)                     /*   query operation to perform         */
{
     if (dfa == NULL) {
          ASSERT(dfa != NULL);
          return(FALSE);
     }
     if (keynum < 0) {
          ASSERT(keynum == -1);
          keynum=dfa->lastkn;
     }
     else {
          dfa->lastkn=keynum;
     }
     if (key != NULL) {
          movmem(key,dfa->key,goodblk(key,dfa->keylns[keynum]));
     }
     if (btvu(qryopt,0,dfa->keyseg,keynum,dfa->reclen) != 0) {
          if (status == 4 || status == 9) {
               return(FALSE);
          }
          (*dfaErrPtr)("QUERY");
     }
     return(TRUE);
}

GBOOL                              /*   return TRUE=success                */
dfaQueryNP(                        /* datafile query next/previous         */
USHORT qryopt)                     /*   operation to perform               */
{
     if (dfa == NULL) {
          ASSERT(dfa != NULL);
          return(FALSE);
     }
     ASSERT(qryopt >= 55 && qryopt <= 63);
     if (btvu(qryopt-50,gpbseg,dfa->keyseg,dfa->lastkn,dfa->reclen) != 0) {
          if (status == 4 || status == 9) {
               return(FALSE);
          }
          dfaPosError(NULL,"QUERY-NP");
     }
     movmem(gpbptr,dfa->data,dfa->reclen);
     return(TRUE);
}

VOID
dfaGetLock(                        /* datafile get operation               */
VOID *recptr,                      /*   pointer for data record            */
const VOID *key,                   /*   key to lookup                      */
SHORT keynum,                      /*   key number or -1                   */
USHORT getopt,                     /*   operation to perform               */
USHORT loktyp)                     /*   lock bias (Btrieve)                */
{
     if (dfa == NULL) {
          ASSERT(dfa != NULL);
          return;
     }
     if (recptr == NULL) {
          recptr=dfa->data;
     }
     if (keynum < 0) {
          ASSERT(keynum == -1);
          keynum=dfa->lastkn;
     }
     else {
          dfa->lastkn=keynum;
     }
     if (key != NULL) {
          movmem(key,dfa->key,goodblk(key,dfa->keylns[keynum]));
     }
     ASSERT(getopt >= 5 && getopt <= 13);
     ASSERT(loktyp == 0 || loktyp == SLWTBV || loktyp == SLNWBV
         || loktyp == MLWTBV || loktyp == MLNWBV);
     if (btvu(getopt+loktyp,gpbseg,dfa->keyseg,keynum,dfa->reclen) != 0) {
          dfaPosError(recptr,"GET");
     }
     movmem(gpbptr,recptr,dfadatptr->dbflen);
}

GBOOL                              /*   return TRUE=success                */
dfaAcqLock(                        /* datafile acquire operation           */
VOID *recptr,                      /*   pointer for data record            */
const VOID *key,                   /*   key to lookup                      */
SHORT keynum,                      /*   key number or -1                   */
USHORT obtopt,                     /*   operation to perform               */
USHORT loktyp)                     /*   lock bias (Btrieve)                */
{
     GBOOL retval;

     if (dfa == NULL) {
          ASSERT(dfa != NULL);
          return(FALSE);
     }
     if (recptr == NULL) {
          recptr=dfa->data;
     }
     if (keynum < 0) {
          ASSERT(keynum == -1);
          keynum=dfa->lastkn;
     }
     else {
          dfa->lastkn=keynum;
     }
     if (key != NULL) {
          movmem(key,dfa->key,goodblk(key,dfa->keylns[keynum]));
     }
     retval=TRUE;
     ASSERT(obtopt >= 5 && obtopt <= 13);
     ASSERT(loktyp == 0 || loktyp == SLWTBV || loktyp == SLNWBV
         || loktyp == MLWTBV || loktyp == MLNWBV);
     if (btvu(obtopt+loktyp,gpbseg,dfa->keyseg,keynum,dfa->reclen) != 0) {
          if (status == 4 || status == 9 || dfaWasLocked()) {
               retval=FALSE;
          }
          else {
               dfaPosError(recptr,"OBTAIN");
          }
     }
     if (retval) {
          movmem(gpbptr,recptr,dfadatptr->dbflen);
     }
     return(retval);
}

GBOOL                              /*   return TRUE=success                */
dfaAcqNPLock(                      /* datafile acquire nxt/prv op          */
VOID *recptr,                      /*   pointer for data record            */
GBOOL chkcas,                      /*   case sensitive?                    */
USHORT anpopt,                     /*   operation to perform               */
USHORT loktyp)                     /*   lock bias (Btrieve)                */
{
     if (dfa == NULL) {
          ASSERT(dfa != NULL);
          return(FALSE);
     }
     movmem(dfa->key,dfa->data,dfa->keylns[dfa->lastkn]);
     if (dfaAcqLock(recptr,NULL,-1,anpopt,loktyp)) {
          if (chkcas) {
               return(strcmp(dfa->data,dfa->key) == 0);
          }
          return(stricmp(dfa->data,dfa->key) == 0);
     }
     return(FALSE);
}

USHORT                             /*   returns length of last record      */
dfaLastLen(VOID)                   /* get length of last record            */
{
     return(lastlen);
}

LONG                               /*   returns absolute position          */
dfaAbs(VOID)                       /* get absolute position of current rec */
{
     if (dfa == NULL) {
          return(0L);
     }
     maksur(sizeof(LONG));
     if (btvu(22,gpbseg,0,0,sizeof(LONG)) != 0) {
          (*dfaErrPtr)("ABSOLUTE");
     }
     return(*(LONG *)gpbptr);
}

VOID
dfaGetAbsLock(                     /* datafile get absolute operation      */
VOID *recptr,                      /*   pointer for data record            */
LONG abspos,                       /*   absolute datafile position         */
SHORT keynum,                      /*   key number (>=0 only)              */
USHORT loktyp)                     /*   lock bias (Btrieve)                */
{
     ASSERT(keynum >= 0);
     if (!dfaAcqAbsLock(recptr,abspos,keynum,loktyp)) {
          dfaPosError(recptr,"GET-ABSOLUTE");
     }
}

GBOOL                              /*   return TRUE=success                */
dfaAcqAbsLock(                     /* datafile acquire absolute operation  */
VOID *recptr,                      /*   pointer for data record            */
LONG abspos,                       /*   absolute datafile position         */
SHORT keynum,                      /*   key number (>=0 only)              */
USHORT loktyp)                     /*   lock bias (Btrieve)                */
{
     ASSERT(dfa != NULL);
     if (recptr == NULL) {
          recptr=dfa->data;
     }
     *(LONG *)gpbptr=abspos;
     ASSERT(keynum >= 0);
     dfa->lastkn=keynum;
     ASSERT(loktyp == 0 || loktyp == SLWTBV || loktyp == SLNWBV
         || loktyp == MLWTBV || loktyp == MLNWBV);
     btvu(23+loktyp,gpbseg,dfa->keyseg,keynum,dfa->reclen);
     if (status == 22) {
          gpbptr[dfa->reclen-1]='\0';
          status=0;
     }
     if (status == 0) {
          movmem(gpbptr,recptr,dfadatptr->dbflen);
     }
     return(status == 0);
}

GBOOL                              /*   return TRUE=success                */
dfaStepLock(                       /* datafile step operation              */
VOID *recptr,                      /*   pointer for data record            */
USHORT stpopt,                     /*   operation to perform               */
USHORT loktyp)                     /*   lock bias (Btrieve)                */
{
     if (dfa == NULL) {
          ASSERT(dfa != NULL);
          return(FALSE);
     }
     if (recptr == NULL) {
          recptr=dfa->data;
     }
     ASSERT(stpopt == 24 || (stpopt >= 33 && stpopt <= 35));
     ASSERT(loktyp == 0 || loktyp == SLWTBV || loktyp == SLNWBV
         || loktyp == MLWTBV || loktyp == MLNWBV);
     if (btvu(stpopt+loktyp,gpbseg,NULL,0,dfa->reclen) != 0) {
          if (status == 9 || dfaWasLocked()) {
               return(FALSE);
          }
          else {
               dfaPosError(recptr,"STEP");
          }
     }
     movmem(gpbptr,recptr,dfadatptr->dbflen);
     return(TRUE);
}

VOID
dfaUpdate(                         /* datafile update record               */
VOID *recptr)                      /*   pointer to record to update        */
{
     ASSERT(dfa != NULL);
     dfaUpdateV(recptr,dfa->reclen);
}

VOID
dfaUpdateV(                        /* datafile update var len record       */
VOID *recptr,                      /*   pointer to record to update        */
USHORT length)                     /*   length of record                   */
{
     ASSERT(dfa != NULL);
     if (recptr == NULL) {
          recptr=dfa->data;
     }
     maksur(length);
     movmem(recptr,gpbptr,goodblk(recptr,length));
     if (btvu(3,gpbseg,dfa->keyseg,dfa->lastkn,length) != 0) {
          (*dfaErrPtr)("UPDATE");
     }
}

GBOOL                              /*   return TRUE=success                */
dfaUpdateDup(                      /* datafile update record (w/o catastro)*/
VOID *recptr)                      /*   pointer to record to update        */
{
     USHORT length;

     if (dfa == NULL) {
          ASSERT(dfa != NULL);
          return(FALSE);
     }
     length=dfa->reclen;
     if (recptr == NULL) {
          recptr=dfa->data;
     }
     maksur(length);
     movmem(recptr,gpbptr,goodblk(recptr,length));
     switch (btvu(3,gpbseg,dfa->keyseg,dfa->lastkn,dfa->reclen)) {
     case 0:
          return(TRUE);
     case 5:
          break;
     default:
          (*dfaErrPtr)("UPDATE");
     }
     return(FALSE);
}

VOID
dfaInsert(                         /* datafile insert record               */
VOID *recptr)                      /*   pointer to record to insert        */
{
     ASSERT(dfa != NULL);
     dfaInsertV(recptr,dfa->reclen);
}

VOID
dfaInsertV(                        /* datafile insert variable length rec. */
VOID *recptr,                      /*   pointer to record to insert        */
USHORT length)                     /*   length of record                   */
{
     ASSERT(dfa != NULL);
     if (recptr == NULL) {
          recptr=dfa->data;
     }
     maksur(length);
     movmem(recptr,gpbptr,goodblk(recptr,length));
     if (btvu(2,gpbseg,dfa->keyseg,0,length) != 0) {
          (*dfaErrPtr)("INSERT");
     }
}

GBOOL                              /*   return TRUE=success                */
dfaInsertDup(                      /* datafile insert record (w/o catastro)*/
VOID *recptr)                      /*   pointer to record to insert        */
{
     USHORT length;

     ASSERT(dfa != NULL);
     length=dfa->reclen;
     if (recptr == NULL) {
          recptr=dfa->data;
     }
     maksur(length);
     movmem(recptr,gpbptr,goodblk(recptr,length));
     switch (btvu(2,gpbseg,dfa->keyseg,0,dfa->reclen)) {
     case 0:
          return(TRUE);
     case 5:
          break;
     default:
          (*dfaErrPtr)("INSERT");
     }
     return(FALSE);
}

VOID
dfaDelete(VOID)                    /* datafile delete record               */
{
     ASSERT(dfa != NULL);
     if (btvu(4,0,0,dfa->lastkn,dfa->reclen) != 0) {
          (*dfaErrPtr)("DELETE");
     }
}

VOID
dfaClose(                          /* close datafile                       */
struct dfablk *dfap)               /*   pointer to dfafile to close        */
{
     CHAR *filnam;

     ASSERT(dfap != NULL);
     if ((dfa=dfap) != NULL && dfa->filnam != NULL) {
          filnam=dfa->filnam;
          dfa->filnam=NULL;
          if (btvu(1,0,0,0,0) != 0) {
               catastro("BTRIEVE CLOSE ERROR %d ON FILE \"%s\"",status,filnam);
          }
          DosFreeSeg(FP_SEG(dfa->key));
          free(dfa->data);
          free(filnam);
          DosFreeSeg(FP_SEG(dfa));
     }
}

VOID
dfaCreateSpec(                     /* create file from spec                */
const CHAR *fileName,              /*   path and name of file to create    */
GBOOL overwrite,                   /*   overwrite existing file?           */
size_t recordLength,               /*   fixed-size record length           */
size_t pageSize,                   /*   page size (must be multiple of 512)*/
INT flags,                         /*   file creation flags                */
INT nPreAllocate,                  /*   number of pages to pre-allocate    */
INT nKeys,                         /*   number of keys (not segments)      */
struct dfaKeySpec *keys,           /*   array of key info                  */
const CHAR *altFile)               /*   alternating colating sequence file */
{
     INT iKey,iSeg;
     GBOOL useACS;
     size_t bufsiz;
     CHAR *buf,*bufptr;
     struct dfaCreateFileSpec fs;
     struct dfaCreateKeySpec ks;
     FILE *fp;

     useACS=FALSE;
     bufsiz=sizeof(struct dfaCreateFileSpec);
     for (iKey=0 ; iKey < nKeys ; ++iKey) {
          for (iSeg=0 ; iSeg < keys[iKey].nSegments ; ++iSeg) {
               bufsiz+=sizeof(struct dfaCreateKeySpec);
               if (keys[iKey].segs[iSeg].flags&DFASF_ALTCOLLATE) {
                    ASSERT(altFile != NULL);
                    useACS=TRUE;
               }
          }
     }
     if (useACS) {
          bufsiz+=ACSSIZ;
     }
     bufptr=buf=alcmem(bufsiz);

     memset(&fs,0,sizeof(struct dfaCreateFileSpec));
     fs.recordLength=(USHORT)recordLength;
     fs.pageSize=(USHORT)pageSize;
     fs.nKeys=(USHORT)nKeys;
     fs.nPreAllocate=(USHORT)nPreAllocate;
     fs.flags=flags&DFACF_USERVALID;
     if (nPreAllocate != 0) {
          fs.flags|=DFACF_PREALLOC;
     }
     memcpy(bufptr,&fs,sizeof(struct dfaCreateFileSpec));
     bufptr+=sizeof(struct dfaCreateFileSpec);

     for (iKey=0 ; iKey < nKeys ; ++iKey) {
          for (iSeg=0 ; iSeg < keys[iKey].nSegments ; ++iSeg) {
               memset(&ks,0,sizeof(struct dfaCreateKeySpec));
               ks.position=(USHORT)(keys[iKey].segs[iSeg].position+1);
               ks.length=(USHORT)keys[iKey].segs[iSeg].length;
               ks.extendedType=(UCHAR)keys[iKey].segs[iSeg].type;
               ks.flags=DFASF_EXTENDED;
               if (iSeg < keys[iKey].nSegments-1) {
                    ks.flags|=DFASF_SEGMENT;
               }
               ks.flags|=(USHORT)(keys[iKey].flags&DFASF_KEYVALID);
               ks.flags|=(USHORT)(keys[iKey].segs[iSeg].flags&DFASF_USERVALID);
               if (ks.flags&DFAKF_NULL) {
                    ks.nullChar=keys[iKey].segs[iSeg].nullChar;
               }
               memcpy(bufptr,&ks,sizeof(struct dfaCreateKeySpec));
               bufptr+=sizeof(struct dfaCreateKeySpec);
          }
     }
     if (useACS) {
          if ((fp=fopen(altFile,FOPRB)) == NULL) {
               free(buf);
               catastro("Unable to open alternate collating sequence file");
          }
          if (fread(bufptr,1,ACSSIZ,fp) != ACSSIZ) {
               free(buf);
               catastro("Unable to read alternate collating sequence file");
          }
          fclose(fp);
     }

     dfaCreate(fileName,buf,overwrite ? 0 : -1,bufsiz);
     free(buf);
}

VOID
dfaCreate(                         /* create datafile                      */
const VOID *filnam,                /*   filename to create                 */
VOID *databuf,                     /*   pointer to databuffer              */
SHORT keyno,                       /*   number of keys                     */
USHORT lendbuf)                    /*   length of data buffer              */
{
     USHORT tseg;
     DFAFILE *crtdfa=NULL;

     eclalcrem(sizeof(DFAFILE),&tseg,(CHAR **)&crtdfa);
     setmem(crtdfa,sizeof(DFAFILE),0);
     crtdfa->realseg=tseg;
     crtdfa->reclen=lendbuf;
     maksur(lendbuf);
     movmem(databuf,gpbptr,goodblk(databuf,lendbuf));
     eclalcrem(strlen(filnam)+1,&crtdfa->keyseg,&crtdfa->key);
     strcpy(crtdfa->key,filnam);
     dfaSetBlk(crtdfa);
     if (btvu(14,gpbseg,crtdfa->keyseg,keyno,crtdfa->reclen) != 0) {
          crtdfa->filnam=(CHAR *)filnam;
          (*dfaErrPtr)("CREATE");
     }
     dfaRstBlk();
     DosFreeSeg(FP_SEG(crtdfa->key));
     DosFreeSeg(FP_SEG(crtdfa));
}

ULONG                              /*    returns number of records         */
dfaCountRec(VOID)                  /* count records in datafile            */
{
     CHAR *tmp;
     static USHORT kbfseg=0;

     if (kbfseg == 0) {
          eclalcrem(64,&kbfseg,&tmp);
     }
     maksur(sizeof(struct statbf));
     if (btvu(15,gpbseg,kbfseg,0,sizeof(struct statbf)) != 0) {
          (*dfaErrPtr)("STAT");
     }
     return(GIBP->fs.numofr);
}

USHORT                             /*   returns record length              */
dfaRecLen(VOID)                    /* get record length of current record  */
{
     CHAR *tmp;
     static USHORT kbfseg=0;

     if (kbfseg == 0) {
          eclalcrem(64,&kbfseg,&tmp);
     }
     if (btvu(15,gpbseg,kbfseg,0,sizeof(struct statbf)) != 0) {
          (*dfaErrPtr)("STAT");
     }
     return(GIBP->fs.reclen);
}

VOID
dfaUnlock(                         /* unlock record                        */
LONG abspos,                       /*   position to unlock                 */
SHORT keynum)                      /*   key number or -1                   */
{
     if (keynum == -1) {
          maksur(sizeof(LONG));
          *(LONG *)gpbptr=abspos;
          btvu(27,gpbseg,0,keynum,sizeof(LONG));
     }
     else {
          btvu(27,0,0,keynum,0);
     }
     if (status != 0) {
          (*dfaErrPtr)("UNLOCK");
     }
}

GBOOL                              /*   return TRUE=record/file was locked */
dfaWasLocked(VOID)                 /* check record lock status             */
{
     return(status == 84 || status == 85);
}

GBOOL                              /*   returns TRUE=success               */
dfaVirgin(                         /* copy virgin file into database file  */
const CHAR *src,                   /*   virgin database file name w/o ext  */
const CHAR *dst)                   /*   destination file name or NULL      */
{
     CHAR srcfil[GCMAXPTH];
     CHAR dstfil[GCMAXPTH];

     sprintf(srcfil,GCVIRGIN"%s.vir",src);
     sprintf(dstfil,"%s.dat",(dst == NULL) ? src : dst);
     return(dfaCopyFile(srcfil,dstfil));
}

VOID
dfaStop(VOID)                      /* stop btrieve operation               */
{
     btvu(25,0,0,0,0);
}

static VOID                        /* don't crash on overflow, truncate    */
dfaPosError(                       /* possible btrieve error               */
CHAR *recptr,                      /*   current record buffer              */
const CHAR *stg)                   /*   error string to display            */
{
     if (status != 22) {
          (*dfaErrPtr)(stg);
     }
     if (recptr == NULL) {
          dfa->data[dfa->reclen-1]='\0';
     }
     else {
          gpbptr[dfa->reclen-1]='\0';
     }
     status=0;
}

static VOID
dfaError(                          /* print out any btrieve error msg      */
const CHAR *who)                   /*   btrieve call responsible for error */
{
     char buff[40];

     sprintf(buff,"%s ERROR %d",who,status);
     catastro("BTRIEVE %s ON FILE \"%s\"",buff,dfa->filnam);
}

static USHORT
btvu(                              /* underlying Btrieve cmd interface     */
USHORT funcno,                     /*   function number                    */
USHORT datbufseg,                  /*   data buffer segment                */
USHORT keyseg,                     /*   key segment                        */
SHORT keyno,                       /*   key number                         */
USHORT rlen)                       /*   record length                      */
{
     REGS16 eclregs;
     static GBOOL btrvup=FALSE,alrcat=FALSE;

     if (!btrvup) {
          setmem(&eclregs,sizeof(REGS16),0);
          eclregs.ax=0x357B;
          eclint(0x21,&eclregs);
          if (eclregs.bx != 0x33) {
               if (alrcat) {
                    return(0);
               }
               alrcat=TRUE;
               catastro("Run BTRIEVE first, before running this program!");
          }
          btrvup=TRUE;
     }
     dfadatptr->datbufoff=0;
     dfadatptr->datbufseg=datbufseg;
     dfadatptr->dbflen=rlen;
     dfadatptr->posp38off=38;
     dfadatptr->posp38seg=dfa->realseg;
     dfadatptr->posblkoff=0;
     dfadatptr->posblkseg=dfa->realseg;
     dfadatptr->funcno=funcno;
     dfadatptr->keyoff=0;
     dfadatptr->keyseg=keyseg;
     dfadatptr->keylen=(char)255;
     dfadatptr->keyno=(char)keyno;
     dfadatptr->statptoff=0;
     dfadatptr->statptseg=statusseg;
     *statusptr=0;
     dfadatptr->magic=24950;
     memset(&eclregs,0,sizeof(REGS16));
     eclregs.dx=0;
     eclregs.ds=dfadatseg;
     eclint(0x7B,&eclregs);
     lastlen=dfadatptr->dbflen;
     return(status=*statusptr);
}

static VOID
eclint(                            /* protected to real interrupt          */
USHORT inum,
REGS16 *regs)
{
     DosRealIntr(inum,regs,0L,0);
}

static GBOOL                       /*   returns TRUE=success               */
dfaCopyFile(                       /* copy file src to dst                 */
const CHAR *src,                   /*   source filename                    */
const CHAR *dst)                   /*   destination filename               */
{
     INT frsiz;
     FILE *fvir,*fnew;
     static CHAR inbuf[4096];

     if ((fvir=fopen(src,FOPRB)) == NULL) {
          return(FALSE);
     }
     if ((fnew=fopen(dst,FOPWB)) == NULL) {
          fclose(fvir);
          return(FALSE);
     }
     while (!feof(fvir)) {
          if ((frsiz=(INT)fread(inbuf,1,4096,fvir)) > 0) {
               fwrite(inbuf,1,frsiz,fnew);
          }
          if (ferror(fnew) || ferror(fvir)) {
               fclose(fvir);
               fclose(fnew);
               unlink(dst);
               return(FALSE);
          }
     }
     fclose(fvir);
     fclose(fnew);
     return(TRUE);
}

static USHORT                 /*   return largest key length               */
clckln(VOID)                  /* calculate all key lengths in curr file    */
{
     CHAR *tmp;
     USHORT i,keyno,klen,maxofa;
     static USHORT kbfseg=0;

     if (kbfseg == 0) {
          eclalcrem(64,&kbfseg,&tmp);
     }
     maksur(sizeof(struct statbf));
     if (btvu(15,gpbseg,kbfseg,0,sizeof(struct statbf)) != 0) {
          (*dfaErrPtr)("STAT");
     }
     maxofa=0;
     for (i=0,keyno=0 ; keyno < GPBP->fs.numofx ; i++,keyno++) {
          klen=GPBP->ks[i].keylen;
          while (GPBP->ks[i].flags&ANOSEG) {
               klen+=GPBP->ks[++i].keylen;
          }
          dfa->keylns[keyno]=klen;
          if (klen > maxofa) {
               maxofa=klen;
          }
     }
     return(maxofa+1);
}
