/***************************************************************************
 *                                                                         *
 *   DFAAPI.C                                                              *
 *                                                                         *
 *   Copyright (c) 1987-1996 Galacticomm, Inc.   All Rights Reserved.      *
 *                                                                         *
 *   This file contains a library of routines for accessing BTRIEVE files  *
 *   with fixed or variable length records, from Windows NT and Large      *
 *   Model 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 "gcommlib.h"

#pragma optimize("",off)
#pragma optimize("ywtap",on)
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 USHORT lastlen;             // length of last record read

#define ANOSEG  0x10               // keyspc.flags for key has another seg

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

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

struct flddef filspcFDA[] = {

     {CVTFLD_SHORT,3,fldoff(filspc,reclen),NULL},
     {CVTFLD_LONG ,1,fldoff(filspc,numofr),NULL},
     {CVTFLD_SHORT,3,fldoff(filspc,flags) ,NULL},
     {CVTFLD_END  ,0,0                    ,NULL}
};

struct flddef keyspcFDA[] = {
     {CVTFLD_SHORT,3,fldoff(keyspc,keypos),  NULL},
     {CVTFLD_LONG, 1,fldoff(keyspc,numofk),  NULL},
     {CVTFLD_CHAR, 2,fldoff(keyspc,extype),  NULL},
     {CVTFLD_LONG, 1,fldoff(keyspc,reserved),NULL},
     {CVTFLD_END  ,0,0                    ,  NULL}
};

struct flddef statbfFDA[] = {
     {CVTFLD_STRUCT,1,     fldoff(statbf,fs),filspcFDA},
     {CVTFLD_STRUCT,SEGMAX,fldoff(statbf,ks),keyspcFDA},
     {CVTFLD_CHAR,  265,   fldoff(statbf,altcol),NULL},
     {CVTFLD_END,   0,     0,                     NULL}
};

#define EXTYPE_INT   1             // extended key segment type INT
#define EXTYPE_UINT  14            // extended key segment type UINT

static USHORT clckln(struct statbf *statInfo);
static VOID dfaPosError(CHAR *recptr,CHAR *stg);
static VOID dfaError(CHAR *who);
static GBOOL dfaCopyFile(CHAR *src,CHAR *dst);
static USHORT btvu(USHORT funcno,VOID *datbuf,VOID *key,
                                   SHORT keyno,USHORT rlen);

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

static USHORT buildFDA(struct statbf *statInfo,USHORT keyLength[],
                       struct flddef *fdaList[],USHORT unpackKeySiz[]);
static USHORT getFDAfld(USHORT keytype,USHORT keylen,struct flddef *flddef,
                        USHORT *fldidx);

extern "C" {
     DllImport int APIENTRY BTRCALL(USHORT operation,VOID *posBlock,VOID *dataBuffer,
                                ULONG *dataLength,VOID *keyBuffer,CHAR keyLength,CHAR ckeynum);
};

DFAFILE *                          //   pointer to a dfa file structure
dfaOpen(                           // open data file
CHAR *filnam,                      //   filename
USHORT maxlen,                     //   maximum record length
CHAR *owner)                       //   owner of file (NULL if none)
{
     struct statbf statInfo;
     USHORT ownlen,stt,cntr;

     dfa=(struct dfablk *)alcmem(sizeof(struct dfablk));
     dfa->filnam=(CHAR *)alcmem(strlen(filnam)+1);
     strcpy(dfa->filnam,filnam);
     dfa->reclen=maxlen;
     dfa->data=(CHAR *)alcmem(maxlen);
     if (owner == NULL) {
          ownlen=0;
     }
     else {
          ownlen=1+strlen(owner);
     }
     cntr=0;
     while (1) {
          stt=btvu(0,owner,filnam,dfaomode,ownlen);
          if (stt == 20) {
               if (++cntr > 100) { // HACK - retries for a while if the
                    break;         //        btrieve engine isn't ready
               }
          }
          else if (stt != 85) {    // file is locked by another process -
               break;              // loop until file is unlocked
          }
     }
     if (status != 0) {
          (*dfaErrPtr)("OPEN");
     }
     clckln(&statInfo);
     dfa->key=(CHAR *)alcmem(buildFDA(&statInfo,dfa->keylns,dfa->flddefList,dfa->unpackKeySiz));
     dfaSetBlk(dfa);
     return(dfa);
}

VOID                               //   Btrieve-specific function
dfaMode(                           // set data file mode
SHORT mode)                        //   access mode
{
     dfaomode=mode;
}

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

VOID
dfaRstBlk(VOID)                    // restore last dfablk pointer
{
     memmove(dfastk,dfastk+1,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,NULL,NULL,0,0) != 0) {
          (*dfaErrPtr)("BEGIN-XACTION");
     }
}

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

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

GBOOL                              //   return TRUE=success
dfaQuery(                          // datafile query operation
VOID *key,                         //   key to lookup
SHORT keynum,                      //   key number or -1
USHORT qryopt)                     //   query operation to perform
{
     GBOOL retval;

     if (dfa == NULL) {
          assert(dfa != NULL);
          return(FALSE);
     }
     if (keynum < 0) {
          assert(keynum == -1);
          keynum=dfa->lastkn;
     }
     else {
          dfa->lastkn=keynum;
     }
     if (key != NULL) {
          memmove(dfa->key,key,dfa->unpackKeySiz[keynum]);
     }
     if (dfa->flddefList[keynum] != NULL) {
          cvtDataIP(dfa->key,dfa->unpackKeySiz[keynum],dfa->keylns[keynum],
                    dfa->flddefList[keynum],CVTSERVER,CVTDFA,CHAN_NUL);
     }
     retval=TRUE;
     if (btvu(qryopt,NULL,dfa->key,keynum,dfa->reclen) != 0) {
          if (status == 4 || status == 9) {
               retval=FALSE;
          }
          else {
               (*dfaErrPtr)("QUERY");
          }
     }
     if (dfa->flddefList[keynum] != NULL) {
          cvtDataIP(dfa->key,dfa->keylns[keynum],dfa->unpackKeySiz[keynum],
                    dfa->flddefList[keynum],CVTDFA,CVTSERVER,CHAN_NUL);
     }
     return(retval);
}

GBOOL                              //   return TRUE=success
dfaQueryNP(                        // datafile query next/previous
USHORT qryopt)                     //   operation to perform
{
     GBOOL retval;

     if (dfa == NULL) {
          assert(dfa != NULL);
          return(FALSE);
     }
     if (dfa->flddefList[dfa->lastkn] != NULL) {
          cvtDataIP(dfa->key,dfa->unpackKeySiz[dfa->lastkn],
                    dfa->keylns[dfa->lastkn],dfa->flddefList[dfa->lastkn],
                    CVTSERVER,CVTDFA,CHAN_NUL);
     }
     retval=TRUE;
     assert(qryopt >= 55 && qryopt <= 63);
     if (btvu(qryopt-50,dfa->data,dfa->key,dfa->lastkn,dfa->reclen) != 0) {
          if (status == 4 || status == 9) {
               retval=FALSE;
          }
          else {
               dfaPosError(NULL,"QUERY-NP");
          }
     }
     if (dfa->flddefList[dfa->lastkn] != NULL) {
          cvtDataIP(dfa->key,dfa->keylns[dfa->lastkn],
                    dfa->unpackKeySiz[dfa->lastkn],dfa->flddefList[dfa->lastkn],
                    CVTDFA,CVTSERVER,CHAN_NUL);
     }
     return(retval);
}

VOID
dfaGetLock(                        // datafile get operation
VOID *recptr,                      //   pointer for data record
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) {
          memmove(dfa->key,key,dfa->unpackKeySiz[keynum]);
     }
     if (dfa->flddefList[keynum] != NULL) {
          cvtDataIP(dfa->key,dfa->unpackKeySiz[keynum],dfa->keylns[keynum],
                    dfa->flddefList[keynum],CVTSERVER,CVTDFA,CHAN_NUL);
     }
     assert(getopt >= 5 && getopt <= 13);
     assert(loktyp == 0 || loktyp == SLWTBV || loktyp == SLNWBV
         || loktyp == MLWTBV || loktyp == MLNWBV);
     if (btvu(getopt+loktyp,recptr,dfa->key,keynum,dfa->reclen) != 0) {
          dfaPosError((CHAR *)recptr,"GET");
     }
     if (dfa->flddefList[keynum] != NULL) {
          cvtDataIP(dfa->key,dfa->keylns[keynum],dfa->unpackKeySiz[keynum],
                    dfa->flddefList[keynum],CVTDFA,CVTSERVER,CHAN_NUL);
     }
}

GBOOL                              //   return TRUE=success
dfaAcqLock(                        // datafile acquire operation
VOID *recptr,                      //   pointer for data record
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) {
          memmove(dfa->key,key,dfa->unpackKeySiz[keynum]);
     }
     if (dfa->flddefList[keynum] != NULL) {
          cvtDataIP(dfa->key,dfa->unpackKeySiz[keynum],dfa->keylns[keynum],
                    dfa->flddefList[keynum],CVTSERVER,CVTDFA,CHAN_NUL);
     }
     retval=TRUE;
     assert(obtopt >= 5 && obtopt <= 13);
     assert(loktyp == 0 || loktyp == SLWTBV || loktyp == SLNWBV
         || loktyp == MLWTBV || loktyp == MLNWBV);
     if (btvu(obtopt+loktyp,recptr,dfa->key,keynum,dfa->reclen) != 0) {
          if (status == 4 || status == 9 || dfaWasLocked()) {
               retval=FALSE;
          }
          else {
               dfaPosError((CHAR *)recptr,"OBTAIN");
          }
     }
     if (dfa->flddefList[keynum] != NULL) {
          cvtDataIP(dfa->key,dfa->keylns[keynum],dfa->unpackKeySiz[keynum],
                    dfa->flddefList[keynum],CVTDFA,CVTSERVER,CHAN_NUL);
     }
     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);
     }
     memmove(dfa->data,dfa->key,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 
{
     LONG abspos;
     if (btvu(22,&abspos,NULL,0,sizeof(LONG)) != 0) {
          (*dfaErrPtr)("ABSOLUTE");
     }
     return(abspos);
}

VOID
dfaGetAbsLock(                     // datafile get absolute operation      
VOID *recptr,                      //   pointer for data record            
LONG abspos,                       //   absolute datafile position         
SHORT keynum,                      //   key number                         
USHORT loktyp)                     //   lock bias (Btrieve)                
{
     assert(keynum >= 0);
     if (!dfaAcqAbsLock(recptr,abspos,keynum,loktyp)) {
          dfaPosError((CHAR *)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                         
USHORT loktyp)                     //   lock bias (Btrieve)                
{
     assert(dfa != NULL);
     if (recptr == NULL) {
          recptr=dfa->data;
     }
     *(LONG *)recptr=abspos;
     assert(keynum >= 0);
     dfa->lastkn=keynum;
     assert(loktyp == 0 || loktyp == SLWTBV || loktyp == SLNWBV
         || loktyp == MLWTBV || loktyp == MLNWBV);
     btvu(23+loktyp,recptr,dfa->key,keynum,dfa->reclen);
     if (status == 22) {
          if (recptr == NULL) {
               dfa->data[dfa->reclen-1]='\0';
          }
          else {
               ((CHAR *)recptr)[dfa->reclen-1]='\0';
          }
          status=0;
     }
     if (dfa->flddefList[keynum] != NULL) {
          cvtDataIP(dfa->key,dfa->keylns[keynum],dfa->unpackKeySiz[keynum],
                    dfa->flddefList[keynum],CVTDFA,CVTSERVER,CHAN_NUL);
     }
     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,recptr,NULL,0,dfa->reclen) != 0) {
          if (status == 9 || dfaWasLocked()) {
               return(FALSE);
          }
          else {
               dfaPosError((CHAR *)recptr,"STEP");
          }
     }
     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;
     }
     if (btvu(3,recptr,dfa->key,dfa->lastkn,length) != 0) {
          (*dfaErrPtr)("UPDATE");
     }
}

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

     if (dfa == NULL) {
          assert(dfa != NULL);
          return(FALSE);
     }
     if (recptr == NULL) {
          recptr=dfa->data;
     }
     retval=TRUE;
     switch (btvu(3,recptr,dfa->key,dfa->lastkn,dfa->reclen)) {
     case 5:
          retval=FALSE;
          break;
     default:
          (*dfaErrPtr)("UPDATE");
     }
     return(retval);
}

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;
     }
     if (btvu(2,recptr,dfa->key,0,length) != 0) {
          (*dfaErrPtr)("INSERT");
     }
}

GBOOL                              //   return TRUE=success                
dfaInsertDup(                      // datafile insert record (w/o catastro)
VOID *recptr)                      //   pointer to record to insert        
{
     assert(dfa != NULL);
     if (recptr == NULL) {
          recptr=dfa->data;
     }
     switch (btvu(2,recptr,dfa->key,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,NULL,NULL,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,NULL,NULL,0,0) != 0) {
               catastro("BTRIEVE CLOSE ERROR %d ON FILE \"%s\"",status,filnam);
          }
          free(dfa->key);
          free(dfa->data);
          free(filnam);
          free(dfa);
     }
}

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

     crtdfa=(struct dfablk *)alczer(sizeof(DFAFILE));
     crtdfa->key=(CHAR *)filnam;
     crtdfa->data=(CHAR *)databuf;
     crtdfa->reclen=lendbuf;
     dfaSetBlk(crtdfa);
     if (btvu(14,crtdfa->data,crtdfa->key,keyno,crtdfa->reclen) != 0) {
          crtdfa->filnam=(CHAR *)filnam;
          (*dfaErrPtr)("CREATE");
     }
     free(crtdfa);
}

ULONG                              //    returns number of records         
dfaCountRec(VOID)                  // count records in datafile            
{
     CHAR kbf[64];
     struct statbf statbf;

     if (btvu(15,&statbf,kbf,0,sizeof(struct statbf)) != 0) {
          (*dfaErrPtr)("STAT");
     }
     cvtDataIP(&statbf,sizeof(struct statbf),sizeof(struct statbf),
               statbfFDA,CVTDFA,CVTSERVER,CHAN_NUL);
     return(statbf.fs.numofr);
}

USHORT                             //   returns record length              
dfaRecLen(VOID)                    // get record length of current record  
{
     CHAR kbf[64];
     struct statbf statbf;

     if (btvu(15,&statbf,kbf,0,sizeof(struct statbf)) != 0) {
          (*dfaErrPtr)("STAT");
     }
     cvtDataIP(&statbf,sizeof(struct statbf),sizeof(struct statbf),
               statbfFDA,CVTDFA,CVTSERVER,CHAN_NUL);
     return(statbf.fs.reclen);
}

VOID
dfaStat(                           // return stats on current datafile     
USHORT len)                        //   length of buffer                   
{
     assert(dfa != NULL);
     if (btvu(15,dfa->data,dfa->key,0,len) != 0) {
          (*dfaErrPtr)("STAT");
     }
}

VOID
dfaUnlock(                         // unlock record                        
LONG abspos,                       //   position to unlock                 
SHORT keynum)                      //   key number or -1                   
{
     if (keynum == -1) {
          btvu(27,&abspos,NULL,keynum,sizeof(LONG));
     }
     else {
          btvu(27,NULL,NULL,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  
CHAR *src,                         //   virgin database file name w/o ext  
CHAR *dst)                         //   destination file name or NULL      
{
     CHAR srcfil[GCMAXPTH];
     CHAR dstfil[GCMAXPTH];

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

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

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

static VOID
dfaError(                          //   print out any btrieve error msg
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                      //   return Btrieve status              
btvu(                              // underlying Btrieve command interface 
USHORT funcno,                     //   btrieve function number to execute 
VOID *datbuf,                      //   ptr to data buffer                 
VOID *key,                         //   ptr to key buffer                  
SHORT keyno,                       //   key number to work with            
USHORT rlen)                       //   record length                      
{
     status=BTRCALL((USHORT)funcno,(VOID *)dfa->posblk,datbuf,(ULONG*)&rlen,key,(CHAR)255,
                    (CHAR)keyno);
     lastlen=rlen;
     return(status);
}

static GBOOL                       //   returns TRUE=success               
dfaCopyFile(                       // copy file src to dst                 
CHAR *src,                         //   source filename                    
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(                            // calculate all key lens in curr file  
struct statbf *statInfo)           //   buffer for file statistics         
{
     CHAR kbf[64];
     USHORT i,keyno,klen,maxofa;

     if (btvu(15,statInfo,kbf,0,sizeof(struct statbf)) != 0) {
          (*dfaErrPtr)("STAT");
     }
     cvtDataIP(statInfo,sizeof(struct statbf),sizeof(struct statbf),
               statbfFDA,CVTDFA,CVTSERVER,CHAN_NUL);
     maxofa=0;
     for (i=0,keyno=0 ; keyno < statInfo->fs.numofx ; i++,keyno++) {
          klen=statInfo->ks[i].keylen;
          while (statInfo->ks[i].flags&ANOSEG) {
               klen+=statInfo->ks[++i].keylen;
          }
          dfa->keylns[keyno]=klen;
          if (klen > maxofa) {
               maxofa=klen;
          }
     }
     return(maxofa+1);
}


#define MAXKEYSEG  16              // max number of segments per key       

static USHORT                      //   returns largest unpacked key size  
buildFDA(                          // build FDA struct for each key        
struct statbf *statInfo,           //   statistics for current file        
USHORT keyLength[],                //   packed key sizes                   
struct flddef *fdaList[],          //   FDA list for current file          
USHORT unpackKeySiz[])             //   calculated unpacked key size       
{
     struct flddef flddef[MAXKEYSEG];
     USHORT numofkeys,key,seg;
     USHORT fldidx;
     USHORT curKeySiz,maxKeySiz;

     numofkeys=statInfo->fs.numofx;
     maxKeySiz=0;
     if (numofkeys > SEGMAX) {
          catastro("buildFDA: too many keys!");
     }
     for (key=seg=0 ; key < numofkeys ; key++) {
                          // we are not going to use first entry of flddef
          fldidx=0;
          flddef[0].type=CVTFLD_END;
          do {
               if (fldidx > MAXKEYSEG-2) {
                    catastro("buildFDA: too many key segments!");
               }
               curKeySiz=getFDAfld(statInfo->ks[seg].extype,
                                   statInfo->ks[seg].keylen,flddef,&fldidx);
          } while (statInfo->ks[seg++].flags&ANOSEG);
          flddef[++fldidx].type=CVTFLD_END;
          if ((INT)curKeySiz > keyLength[key]) {
               fdaList[key]=(struct flddef *)alcmem(sizeof(struct flddef)*fldidx);
               memmove(fdaList[key],&flddef[1],sizeof(struct flddef)*fldidx);
          }
          else {
               fdaList[key]=NULL;
          }
          unpackKeySiz[key]=curKeySiz;
          if (curKeySiz > maxKeySiz) {
               maxKeySiz=curKeySiz;
          }
     }
     return(maxKeySiz+1);
}

static USHORT                      //   rets current offset within key buf 
getFDAfld(                         // creates FDA entry for single key fld 
USHORT keytype,                    //   type of key to create entry for    
USHORT keylen,                     //   length of the key                  
struct flddef *flddef,             //   FDA to be filled in                
USHORT *fldidx)                    //   index to FDA array                 
{
     static USHORT offset;

     if (flddef[*fldidx].type == CVTFLD_END) {
          offset=0;
     }
     switch (keytype) {
     case EXTYPE_INT:
     case EXTYPE_UINT:
          if (keylen == 2) {
               if (flddef[*fldidx].type == CVTFLD_SHORT) {
                    flddef[*fldidx].nelem++;
               }
               else {
                    if (offset%keylen != 0) {
                         offset+=(USHORT)(keylen-offset%keylen);// add padding
                    }
                    ++(*fldidx);
                    flddef[*fldidx].type=CVTFLD_SHORT;
                    flddef[*fldidx].nelem=1;
                    flddef[*fldidx].offset=offset;
                    flddef[*fldidx].substruct=NULL;
               }
               break;
          }
          else if (keylen == 4) {
               if (flddef[*fldidx].type == CVTFLD_LONG) {
                    flddef[*fldidx].nelem++;
               }
               else {
                    if (offset%keylen != 0) {
                         offset+=(USHORT)(keylen-offset%keylen);// add padding
                    }
                    ++(*fldidx);
                    flddef[*fldidx].type=CVTFLD_LONG;
                    flddef[*fldidx].nelem=1;
                    flddef[*fldidx].offset=offset;
                    flddef[*fldidx].substruct=NULL;
               }
               break;
          }
     default:
          if (flddef[*fldidx].type == CVTFLD_CHAR) {
               flddef[*fldidx].nelem+=keylen;
          }
          else {
               ++(*fldidx);
               flddef[*fldidx].type=CVTFLD_CHAR;
               flddef[*fldidx].nelem=keylen;
               flddef[*fldidx].offset=offset;
               flddef[*fldidx].substruct=NULL;
          }
          break;
     }
     offset+=keylen;
     return(offset);
}

#pragma optimize("",on)