/***************************************************************************
 *                                                                         *
 *   CVTDATF.C                                                             *
 *                                                                         *
 *   Copyright (c) 1996-1997 Galacticomm, Inc.        All Rights Reserved. *
 *                                                                         *
 *   Routines to perform conversion of data file.                          *
 *                                                                         *
 *                                                 - Bill Hyatt 6/25/96    *
 *                                                                         *
 ***************************************************************************/

#ifdef GCWINNT
#include <windows.h>
#endif // GCWINNT
#include <process.h>
#include "gcomm.h"
#include "majorbbs.h"
#include "gcspsrv.h"
#include "cvtapi.h"
#include "dfcapi2.h"
#include "dfcapi.h"

#define FILREV "$Revision: 20 $"

#define NILDELIM '\0'

/* indicators for rdformat() */
#define SOURCE    0                /* reading source format                */
#define DEST      1                /* reading destination format           */

/* indicators for largestfsz */
#define FMT 0                      /* looking thru format info             */
#define FLD 1                      /* looking thru field info              */

struct cvtinf cvtinf;              /* global convert info                  */

GBOOL dbgcvt=FALSE;                /* print cvtinf info? (debugging)       */
static GBOOL ok2cvt=TRUE;          /*  Should we be doing CVTs?            */

ULONG totrec=0L,                   /* total # records in current data file */
      currec;                      /* current record # being processed     */

struct dfldinfo {                  /* default field type info              */
     CHAR idstg[6+1];              /*   string id in cvt desc file         */
     USHORT fldtyp;                /*   field type                         */
     USHORT fldlen;                /*   default field length               */
     ULONG cvttyp;                 /*   convert API field type             */
} dfldinf[NFTYP]={                 /* table of default field info          */
     {"CHAR",  T_CHAR,  0,        CVTFLD_CHAR},
     {"SHORT", T_SHORT, SHORTLEN, CVTFLD_SHORT},
     {"LONG",  T_LONG,  LONGLEN,  CVTFLD_LONG},
     {"FLOAT", T_FLOAT, FLOATLEN, CVTFLD_FLOAT},
     {"DOUBLE",T_DOUBLE,DOUBLELEN,CVTFLD_DOUBLE},
     {"VBDATE",T_VBDATE,VBDATELEN,CVTFLD_VBDATE},
     {"DDATE", T_DDATE, DDATELEN, CVTFLD_DDATE},
     {"DTIME", T_DTIME, DTIMELEN, CVTFLD_DTIME},
     {"STRUCT",T_STRUCT,0,        CVTFLD_STRUCT},
     {"OPAQUE",T_OPAQUE,0,        CVTFLD_OPAQUE}
};

static CHAR gendbbuf[GENSIZ];      /* buffer for generic db data           */

DFAFILE *srcbb=NULL,               /* currently open src data file         */
        *dstbb=NULL;               /* currently open dest data file        */

static FILE *cvtfp;

static VOID pcvtinf(CHAR *cvtfil,GBOOL prtfmt);
static VOID freembrs(struct fldinfo *fldptr);
static GBOOL supcvtinf(CHAR *cvtfil,CHAR *srcdatfil,CHAR *dstdatfil,
     GBOOL keepsrc);
static VOID spawncvt(VOID);
static VOID getmfinfo(CHAR *cvtfil);
static VOID rdformat(struct fmtinfo *fmtptr,CHAR *cvtfil,USHORT fmttype);
static VOID gtsrcfld(CHAR *finfstg,struct fldinfo *sfldptr,USHORT *curoffset);
static USHORT getmembers(struct fldinfo *pfldptr,USHORT srcdst);
static VOID gtdstfld(CHAR *finfstg,struct fldinfo *dfldptr,USHORT *curoffset,
     struct fmtinfo *dstfmt);
static VOID supnewdfld(CHAR *finfstg,struct fldinfo *dfldptr);
static VOID setdefval(struct fldinfo *fldptr,CHAR *valbuf);
static VOID supxstdfld(CHAR *finfstg,struct fldinfo *dfldptr,
     struct fldinfo *sfldptr);
static VOID supvlcodfld(CHAR *finfstg,struct fldinfo *dfldptr,
     struct fldinfo *sfldptr);
static VOID supcodfld(CHAR *finfstg,struct fldinfo *dfldptr,
     struct fldinfo *sfldptr);
static GBOOL supgupdinf(CHAR *cvtfil);
static VOID supbufs(VOID);
static USHORT largestfsz(VOID *structptr,USHORT what);
static GBOOL getfinf(CHAR *finfstg,CHAR *inftag,CHAR delim, CHAR *infbuf,
     GBOOL bomb);
static INT fnddfld(CHAR *idstg);
static VOID adjoff(USHORT *curoffset,USHORT boundval);
static VOID fndlin(CHAR *srchline,FILE *fp,CHAR *filnam);
static VOID stpcmt(CHAR *buf);
static VOID procdatfil(VOID);
static GBOOL ckrecshdrm(VOID);
static GBOOL get1strec(DFAFILE *datbb);
static GBOOL getnxtrec(DFAFILE *datbb);
static VOID procrec(struct fmtinfo *sfmtptr,struct fmtinfo *dfmtptr,
     DFAFILE *dstbb,USHORT lstreclen);
static VOID mov2cvt(struct fldinfo *sfldptr,struct fldinfo *dfldptr);
static struct fldinfo *fndfld(struct fmtinfo *fmtptr,CHAR *fldtag);
static VOID clrcvtbufs(VOID);

MARKSOURCE(cvtdatf);

GBOOL                              /*   returns TRUE if found info         */
readcvt(                           /* read info from conversion desc file  */
CHAR *prefix,                      /*   prefix to look for                 */
CHAR *dest,                        /*   where to put the data              */
CHAR *cvtfil,                      /*   file to search in                  */
GBOOL bomb)                        /*   catastro() if not there?           */
{
     if (tfsopn(cvtfil) == 0) {
          if (bomb) {
               catastro("READCVT: Unable to open %s!",cvtfil);
          }
          return(FALSE);
     }
     while (tfsrdl() != TFSDUN) {
          if (tfstate == TFSLIN && tfspfx(prefix)) {
               stpcmt(tfspst);
               dest[0]='\0';
               stlcpy(dest,tfspst,MAXLSZ+1);
               tfsabt();
               return(TRUE);
          }
     }
     if (bomb) {
          catastro("You must specify \"%s\" in %s!",prefix,cvtfil);
     }
     return(FALSE);
}

GBOOL                              /*   TRUE = should do conversion        */
shouldcvt(                         /* should we do the conversion...       */
CHAR *cvtfil)                      /*   ...described in this cvt desc file?*/
{
     CHAR srcnam[GCMAXFNM];
     GBOOL IsPacked;
     CHAR Packed[100];
     INT Platform;
     CHAR CheckFile[GCMAXPTH];
     CHAR fp[GCMAXPTH];

     clrcvtinf();
     if (!ok2cvt) {
          return(FALSE);
     }
     if (!readcvt("Source:",cvtinf.srcdatfil,cvtfil,FALSE)) {
          return(FALSE);
     }

     if (sameas(cvtinf.srcdatfil,GDBSOURCE)) {
          supgupdinf(cvtfil);
     }
     if (!isfile(cvtinf.srcdatfil)) {
          if (cvtinf.cvttype == DFC_NORMAL) {
               return(FALSE);
          }
          fileparts(GCPART_FILE,cvtinf.srcdatfil,srcnam,sizeof(srcnam));
          strcat(srcnam,"."TMPDATEXT);
          if (!isfile(srcnam)) {
               return(FALSE);
          }
     }
     if (cvtinf.cvttype == DFC_NORMAL
      && !readcvt("Destination:",cvtinf.dstdatfil,cvtfil,FALSE)) {
          return(FALSE);
     }
     if (readcvt("SourcePacked:",Packed,cvtfil,FALSE)) {
          IsPacked=sameas(Packed,"YES");
          Platform=dfcFilePlatform(cvtinf.srcdatfil);
          if (IsPacked && Platform != DFC_DOS) {
               return(FALSE);
          }
          if (!IsPacked && Platform != DFC_WNT) {
               return(FALSE);
          }
          setmem(CheckFile,GCMAXPTH,0);
          setmem(fp,GCMAXPTH,0);
          fileparts(GCPART_FILE,cvtinf.dstdatfil,fp,GCMAXPTH);
          sprintf(CheckFile,"GCVIRDAT\\%s.VIR",fp);

          if (!readcvt("DestPacked:",Packed,cvtfil,FALSE)) {
               return(FALSE);
          }
          IsPacked=sameas(Packed,"YES");
          Platform=dfcFilePlatform(CheckFile);
#ifdef GCWINNT
          if (Platform != DFC_WNT || IsPacked == TRUE) {
               return(FALSE);
          }
#endif
#ifdef GCDOS
          if (Platform != DFC_DOS || IsPacked == FALSE) {
               return(FALSE);
          }
#endif
     }

     readcvt("Description:",cvtinf.cvtdsc,cvtfil,TRUE);
     return(TRUE);
}

#ifdef GCWINNT
VOID
WriteOldRegistry(VOID)             /*  Write old registry information      */
{
     HKEY hKey;
     DWORD disposition;
     CHAR *Setting;

     Setting=getUpdType();

     if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,
      "SOFTWARE\\Galacticomm\\Worldgroup\\3.00\\Settings",
      0,"",0,KEY_ALL_ACCESS ,NULL,
      &hKey,&disposition) != ERROR_SUCCESS) {
          ok2cvt=FALSE;
     }
     RegSetValueEx(hKey,"UpgradeType",0,REG_SZ,Setting,strlen(Setting));
     RegCloseKey(hKey);
}

CHAR *
getUpdType(VOID)
{
     HKEY hKey;
     static CHAR upgradetype[sizeof("n.n to NT")+1];
     DWORD bufsize;

     strcpy(upgradetype,"2.0 to NT");
     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,(LPSTR)REG_KEY_BASE,
      (DWORD)0,KEY_ALL_ACCESS,(PHKEY)&hKey) == ERROR_SUCCESS) {
          bufsize=(DWORD)sizeof(upgradetype);
          if (RegQueryValueEx(hKey,(LPTSTR)"UpgradeType",NULL,NULL,
           (LPBYTE)upgradetype,(LPDWORD)&bufsize) != ERROR_SUCCESS) {
               ok2cvt=FALSE;
               strcpy(upgradetype,"2.0 to NT");
          }
     }
     else {
          ok2cvt=FALSE;
     }
     RegCloseKey(hKey);
     //  This should not happen now
     if (!(sameas(upgradetype,"2.0 to NT")
        || sameas(upgradetype,"3.0 to NT"))) {
          catastro("GETUPDTYPE: Invalid value \"%s\" in UpgradeType setting!",
                   upgradetype);
     }
     return(upgradetype);
}
#endif // GCWINNT

VOID
chkSrcDstNams(                     /* mk tmp src name if src & dst are same*/
CHAR *srcdatfil,                   /*   source data file name              */
CHAR *dstdatfil,                   /*   destination data file name         */
CHAR **tmpsrcnam)                  /*   pointer to location for temp name  */
{
     CHAR *extptr;

     *tmpsrcnam=NULL;
     if (sameas(srcdatfil,dstdatfil)) {
          *tmpsrcnam=alczer(GCMAXPTH);
          extptr=strrchr(srcdatfil,'.');
          stlcpy(*tmpsrcnam,srcdatfil,((INT)(extptr-srcdatfil))+1);
          strcat(*tmpsrcnam,"."TMPDATEXT);
     }
#ifdef GCWINNT
     if (*tmpsrcnam == NULL) { // avoid compiler warning under NT
     }
#endif // GCWINNT
}

VOID
cvtdatfil(                         /* perform a data file conversion       */
CHAR *cvtfil,                      /*   conversion description file        */
CHAR *srcdatfil,                   /*   src file (NULL=rd fm cvt desc file)*/
CHAR *dstdatfil,                   /*   dest file (NULL=    "       "      */
GBOOL keepsrc)                     /*   keep scoure data file?             */
{
     CHAR wrkbuf[MAXLSZ+1];
     INT i;

     ASSERT(cvtfil != NULL);
     clrcvtinf();
     if (supcvtinf(cvtfil,srcdatfil,dstdatfil,keepsrc)
      && cvtinf.cstcvt[0] != '\0') {
          pcvtinf(cvtfil,FALSE);
          spawncvt();
          /* code below checks to see if spawned utility renamed source
             data file to temp name (following convention of using extension
             of "D_T"), and uses cvtinf.tmpsrcnam to communicate that to
             calling module */
          chkSrcDstNams(cvtinf.srcdatfil,cvtinf.dstdatfil,&cvtinf.tmpsrcnam);
          if (cvtinf.tmpsrcnam != NULL && !isfile(cvtinf.tmpsrcnam)) {
               free(cvtinf.tmpsrcnam);
               cvtinf.tmpsrcnam=NULL;
          }
          return;
     }
     if (readcvt("MultiFormat:",wrkbuf,cvtfil,FALSE)) {
          cvtinf.indoffset=atoi(itemidxd(wrkbuf,0,","));
          if (cvtinf.cvttype == DFC_GENDB && cvtinf.indoffset < GDBDOFF) {
               catastro("CVTDATFIL: For generic database conversions, format "
                        "indicator bytes must have minimum offset of %d!",
                        GDBDOFF);
          }
          cvtinf.indbuflen=atoi(itemidxd(wrkbuf,1,","));
          getmfinfo(cvtfil);
     }
     else {
          cvtinf.nfmts=1;
          cvtinf.srcfmts=(struct fmtinfo *)alczer(sizeof(struct fmtinfo));
          cvtinf.dstfmts=(struct fmtinfo *)alczer(sizeof(struct fmtinfo));
     }
     for (i=0 ; i < cvtinf.nfmts ; i++) {
          rdformat(&cvtinf.srcfmts[i],cvtfil,SOURCE);
          rdformat(&cvtinf.dstfmts[i],cvtfil,DEST);
     }
     supbufs();
     if (dbgcvt) {
          pcvtinf(cvtfil,TRUE);
     }
     procdatfil();
     if (cvtinf.cstcvt[0] != '\0') { // RunAfterConvert
          spawncvt();
     }
}

static VOID
pcvtinf(                           /* print out conversion debug info      */
CHAR *cvtfil,                      /*   current conversion template file   */
GBOOL prtfmt)                      /*   print format info?                 */
{
     FILE *dbgfp;
     INT i;
     struct fldinfo *fldptr;

     if ((dbgfp=fopen(CVTDBGFIL,FOPAA)) == NULL) {
          catastro("Unable to open %s for output!",CVTDBGFIL);
     }
     fprintf(dbgfp,"%s %s\n",ncdate(today()),nctime(now()));
     fprintf(dbgfp,"cvtfil: %s\nsource: %s destination: %s\n\n",
                   cvtfil,cvtinf.srcdatfil,cvtinf.dstdatfil);
     if (!prtfmt) {
          if (cvtinf.cstcvt[0] != '\0') {
               fprintf(dbgfp,"Custom conversion utility: %s\n",
                       cvtinf.cstcvt);
               fprintf(dbgfp,"Parameters: %s\n",cvtinf.cstcmdbuf);
          }
     }
     else {
          fprintf(dbgfp,"source fields:\n");
          for (i=0 ; i < cvtinf.srcfmts->nflds ; i++) {
               fldptr=&cvtinf.srcfmts->fields[i];
               fprintf(dbgfp,"%-10s type: %d ",fldptr->fldtag,fldptr->fldtyp);
               switch (fldptr->fldtyp) {
               case T_CHAR:
                    fprintf(dbgfp,"%-6s","CHAR");
                    break;
               case T_SHORT:
                    fprintf(dbgfp,"%-6s","SHORT");
                    break;
               case T_LONG:
                    fprintf(dbgfp,"%-6s","LONG");
                    break;
               case T_FLOAT:
                    fprintf(dbgfp,"%-6s","FLOAT");
                    break;
               case T_DOUBLE:
                    fprintf(dbgfp,"%-6s","DOUBLE");
                    break;
               case T_VBDATE:
                    fprintf(dbgfp,"%-6s","VBDATE");
                    break;
               case T_DDATE:
                    fprintf(dbgfp,"%-6s","DDATE");
                    break;
               case T_DTIME:
                    fprintf(dbgfp,"%-6s","DTIME");
                    break;
               case T_STRUCT:
                    fprintf(dbgfp,"%-6s","STRUCT");
                    break;
               case T_OPAQUE:
                    fprintf(dbgfp,"%-6s","OPAQUE");
                    break;
               }
               fprintf(dbgfp," offset: %-5d %04X len: %-5d nelems: %-5d\n",
                       fldptr->srcoffset,fldptr->srcoffset,fldptr->fldlen,
                       fldptr->nelems);
          }
          fprintf(dbgfp,"\ndestination fields:\n");
          for (i=0 ; i < cvtinf.dstfmts->nflds ; i++) {
               fldptr=&cvtinf.dstfmts->fields[i];
               fprintf(dbgfp,"%-10s type: %d ",fldptr->fldtag,fldptr->fldtyp);
               switch (fldptr->fldtyp) {
               case T_CHAR:
                    fprintf(dbgfp,"%-6s","CHAR");
                    break;
               case T_SHORT:
                    fprintf(dbgfp,"%-6s","SHORT");
                    break;
               case T_LONG:
                    fprintf(dbgfp,"%-6s","LONG");
                    break;
               case T_FLOAT:
                    fprintf(dbgfp,"%-6s","FLOAT");
                    break;
               case T_DOUBLE:
                    fprintf(dbgfp,"%-6s","DOUBLE");
                    break;
               case T_VBDATE:
                    fprintf(dbgfp,"%-6s","VBDATE");
                    break;
               case T_DDATE:
                    fprintf(dbgfp,"%-6s","DDATE");
                    break;
               case T_DTIME:
                    fprintf(dbgfp,"%-6s","DTIME");
                    break;
               case T_STRUCT:
                    fprintf(dbgfp,"%-6s","STRUCT");
                    break;
               case T_OPAQUE:
                    fprintf(dbgfp,"%-6s","OPAQUE");
                    break;
               }
               fprintf(dbgfp," offset: %-5d %04X len: %-5d nelems: %-5d\n",
                       fldptr->dstoffset,fldptr->dstoffset,fldptr->fldlen,
                       fldptr->nelems);
          }
          if (cvtinf.cstcvt[0] != '\0') {
               fprintf(dbgfp,"\nRunAfterConvert: %s\n",cvtinf.cstcvt);
               fprintf(dbgfp,"Parameters: %s\n",cvtinf.cstcmdbuf);
          }
     }
     fprintf(dbgfp,"\n----------------------------------------------------"
             "----------------------------\n\n");
     fclose(dbgfp);
}

VOID
clrcvtinf(VOID)                    /* reset info in cvtinf                 */
{
     INT i,j;

     if (cvtinf.tmpsrcnam != NULL) {
          free(cvtinf.tmpsrcnam);
     }
     if (cvtinf.fmtibufs != NULL) {
          for (i=0 ; i < cvtinf.nfmts ; i++) {
               if (cvtinf.fmtibufs[i] != NULL) {
                    free(cvtinf.fmtibufs[i]);
               }
          }
          if (cvtinf.fmtibufs != NULL) {
               free(cvtinf.fmtibufs);
          }
     }
     for (i=0 ; i < cvtinf.nfmts ; i++) {
          if (cvtinf.srcfmts[i].fields != NULL) {
               for (j=0 ; j < cvtinf.srcfmts[i].nflds ; j++) {
                    if (cvtinf.srcfmts[i].fields[j].fldtyp == T_STRUCT) {
                         freembrs(&cvtinf.srcfmts[i].fields[j]);
                    }
               }
               free(cvtinf.srcfmts[i].fields);
          }
          if (cvtinf.dstfmts[i].fields != NULL) {
               for (j=0 ; j < cvtinf.dstfmts[i].nflds ; j++) {
                    if (cvtinf.dstfmts[i].fields[j].defval != NULL) {
                         free(cvtinf.dstfmts[i].fields[j].defval);
                    }
                    if (cvtinf.dstfmts[i].fields[j].fldtyp == T_STRUCT) {
                         freembrs(&cvtinf.dstfmts[i].fields[j]);
                    }
               }
               free(cvtinf.dstfmts[i].fields);
          }
          if (cvtinf.dstfmts[i].fmtFDA != NULL) {
               free(cvtinf.dstfmts[i].fmtFDA);
          }
     }
     if (cvtinf.srcfmts != NULL) {
          free(cvtinf.srcfmts);
     }
     if (cvtinf.dstfmts != NULL) {
          free(cvtinf.dstfmts);
     }
     if (cvtinf.srcbuf != NULL) {
          free(cvtinf.srcbuf);
     }
     if (cvtinf.cvtbuf != NULL) {
          free(cvtinf.cvtbuf);
     }
     if (cvtinf.dstbuf != NULL) {
          free(cvtinf.dstbuf);
     }
     setmem(&cvtinf,sizeof(struct cvtinf),0);
}

static VOID
freembrs(                          /* free field info in STRUCT field      */
struct fldinfo *fldptr)            /*   field to free members of           */
{
     INT i;

     if (fldptr->nmembers > 0) {
          if (fldptr->subFDA != NULL) {
               free(fldptr->subFDA);
          }
          for (i=0 ; i < fldptr->nmembers ; i++) {
               if (fldptr->members[i].fldtyp == T_STRUCT) {
                    freembrs(&fldptr->members[i]);
               }
          }
          free(fldptr->members);
     }
}

static GBOOL                       /*   TRUE=is a custom cvt program       */
supcvtinf(                         /* set up initial convert info          */
CHAR *cvtfil,                      /*   conversion description file        */
CHAR *srcdatfil,                   /*   src file (NULL=rd fm cvt desc file)*/
CHAR *dstdatfil,                   /*   dest file (NULL=    "       "      */
GBOOL keepsrc)                     /*   keep scoure data file?             */
{
     CHAR wrkbuf[MAXLSZ+1];
     CHAR *cpKeepDis,*cpSrc,*cpSrcPkd,*cpSrcBytOrd,*cpDst,*cpDstPkd,
          *cpDstBytOrd,cpRecOrd[9],cpRatio[5];
     GBOOL rc=FALSE;

     if (srcdatfil != NULL && sameas(srcdatfil,GDBSOURCE)) {
          rc=supgupdinf(cvtfil);
     }
     else if (!readcvt("CustomConvert:",cvtinf.cstcvt,cvtfil,FALSE)) {
          readcvt("Description:",cvtinf.cvtdsc,cvtfil,FALSE);
          if (srcdatfil == NULL) {
               readcvt("Source:",cvtinf.srcdatfil,cvtfil,TRUE);
          }
          else {
               stlcpy(cvtinf.srcdatfil,srcdatfil,GCMAXPTH);
          }
          readcvt("SourcePacked:",wrkbuf,cvtfil,TRUE);
          cvtinf.srcpacked=(wrkbuf[0] == 'Y' || wrkbuf[0] == 'y');
          readcvt("SourceByteOrder:",wrkbuf,cvtfil,TRUE);
          cvtinf.srcbytord=(sameas(wrkbuf,"LITTLE") ? LITTLE : BIG);
          if (dstdatfil == NULL) {
               readcvt("Destination:",cvtinf.dstdatfil,cvtfil,TRUE);
          }
          else {
               stlcpy(cvtinf.dstdatfil,dstdatfil,GCMAXPTH);
          }
          readcvt("DestPacked:",wrkbuf,cvtfil,TRUE);
          cvtinf.dstpacked=(wrkbuf[0] == 'Y' || wrkbuf[0] == 'y');
          readcvt("DestByteOrder:",wrkbuf,cvtfil,TRUE);
          cvtinf.dstbytord=(sameas(wrkbuf,"LITTLE") ? LITTLE : BIG);
          readcvt("RecordOrder:",wrkbuf,cvtfil,TRUE);
          if (sameto("KEY",wrkbuf)) {
               cvtinf.recorder=KEY;
               cvtinf.keyno=atoi(itemidxd(wrkbuf,1,","));
          }
          else {
               cvtinf.recorder=PHYSICAL;
               cvtinf.keyno=-1;
          }
          cvtinf.variable=FALSE;
          readcvt("Ratio:",wrkbuf,cvtfil,TRUE);
          cvtinf.ratio=atof(wrkbuf);
          cvtinf.indoffset=-1;
          cvtinf.indbuflen=-1;
          if (readcvt("RunAfterConvert:",cvtinf.cstcvt,cvtfil,FALSE)) {
               sprintf(cvtinf.cstcmdbuf,"%s %s %s %s %s %s %s %s %1.1f",
                       keepsrc ? "keep" : "discard",
                       cvtinf.srcdatfil,
                       cvtinf.srcpacked ? "yes" : "no",
                       cvtinf.srcbytord == LITTLE ? "little" : "big",
                       cvtinf.dstdatfil,
                       cvtinf.dstpacked ? "yes" : "no",
                       cvtinf.dstbytord == LITTLE ? "little" : "big",
                       cvtinf.recorder == PHYSICAL ? "physical"
                                                   : spr("key,%d",cvtinf.keyno),
                       cvtinf.ratio);
               if (dbgcvt) {
                    strcat(cvtinf.cstcmdbuf," debug");
               }
          }
     }
     else {
          cpKeepDis=keepsrc ? "keep" : "discard";
          readcvt("Description:",cvtinf.cvtdsc,cvtfil,FALSE);
          if (srcdatfil == NULL) {
               if (readcvt("Source:",cvtinf.srcdatfil,cvtfil,FALSE)) {
                    cpSrc=cvtinf.srcdatfil;
               }
               else {
                    cpSrc=NOTSPEC;
               }
          }
          else {
               stlcpy(cvtinf.srcdatfil,srcdatfil,GCMAXPTH);
               cpSrc=cvtinf.srcdatfil;
          }
          if (readcvt("SourcePacked:",wrkbuf,cvtfil,FALSE)) {
               if (wrkbuf[0] == 'Y' || wrkbuf[0] == 'y') {
                    cvtinf.srcpacked=TRUE;
                    cpSrcPkd="yes";
               }
               else {
                    cvtinf.srcpacked=FALSE;
                    cpSrcPkd="no";
               }
          }
          else {
               cpSrcPkd=NOTSPEC;
          }
          if (readcvt("SourceByteOrder:",wrkbuf,cvtfil,FALSE)) {
               if (sameas(wrkbuf,"LITTLE")) {
                    cvtinf.srcbytord=LITTLE;
                    cpSrcBytOrd="little";
               }
               else {
                    cvtinf.srcbytord=BIG;
                    cpSrcBytOrd="big";
               }
          }
          else {
               cpSrcBytOrd=NOTSPEC;
          }
          if (dstdatfil == NULL) {
               if (readcvt("Destination:",cvtinf.dstdatfil,cvtfil,FALSE)) {
                    cpDst=cvtinf.dstdatfil;
               }
               else {
                    cpDst=NOTSPEC;
               }
          }
          else {
               stlcpy(cvtinf.dstdatfil,dstdatfil,GCMAXPTH);
               cpDst=cvtinf.dstdatfil;
          }
          if (readcvt("DestPacked:",wrkbuf,cvtfil,FALSE)) {
               if (wrkbuf[0] == 'Y' || wrkbuf[0] == 'y') {
                    cvtinf.dstpacked=TRUE;
                    cpDstPkd="yes";
               }
               else {
                    cvtinf.srcpacked=FALSE;
                    cpDstPkd="no";
               }
          }
          else {
               cpDstPkd=NOTSPEC;
          }
          if (readcvt("DestByteOrder:",wrkbuf,cvtfil,FALSE)) {
               if (sameas(wrkbuf,"LITTLE")) {
                    cvtinf.dstbytord=LITTLE;
                    cpDstBytOrd="little";
               }
               else {
                    cvtinf.dstbytord=BIG;
                    cpDstBytOrd="big";
               }
          }
          else {
               cpDstBytOrd=NOTSPEC;
          }
          if (readcvt("RecordOrder:",wrkbuf,cvtfil,FALSE)) {
               if (sameto("KEY",wrkbuf)) {
                    cvtinf.recorder=KEY;
                    cvtinf.keyno=atoi(itemidxd(wrkbuf,1,","));
                    sprintf(cpRecOrd,"key,%d",cvtinf.keyno);
               }
               else {
                    cvtinf.recorder=PHYSICAL;
                    cvtinf.keyno=-1;
                    sprintf(cpRecOrd,"physical");
               }
          }
          else {
               sprintf(cpRecOrd,NOTSPEC);
          }
          cvtinf.variable=FALSE;
          if (readcvt("Ratio:",wrkbuf,cvtfil,FALSE)) {
               cvtinf.ratio=atof(wrkbuf);
               sprintf(cpRatio,"%1.1f",cvtinf.ratio);
          }
          else {
               sprintf(cpRatio,NOTSPEC);
          }
          cvtinf.indoffset=-1;
          cvtinf.indbuflen=-1;
          sprintf(cvtinf.cstcmdbuf,"%s %s %s %s %s %s %s %s %s",
                  cpKeepDis,
                  cpSrc,cpSrcPkd,cpSrcBytOrd,
                  cpDst,cpDstPkd,cpDstBytOrd,
                  cpRecOrd,cpRatio);
          rc=TRUE;
          if (dbgcvt) {
               strcat(cvtinf.cstcmdbuf," debug");
          }
     }
     if (cvtinf.srcbytord == BIG) {
          cvtinf.srccvtflgs|=CVTBIGEND;
     }
     if (cvtinf.srcpacked) {
          cvtinf.srccvtflgs|=CVTPACKED;
     }
     if (cvtinf.dstbytord == BIG) {
          cvtinf.dstcvtflgs|=CVTBIGEND;
     }
     if (cvtinf.dstpacked) {
          cvtinf.dstcvtflgs|=CVTPACKED;
     }
     return(rc);
}

static VOID
spawncvt(VOID)                     /* spawn a custom conversion utility    */
{
#ifdef GCDOS
     INT i,narg;
     CHAR *cp,argstr[CSTCMDBSZ],*argarr[MAXARGS+1+1];

     dfcDisplayInfo(NULL,NULL,NULL,0);

     argarr[0]=cvtinf.cstcvt;
     stlcpy(argstr,cvtinf.cstcmdbuf,CSTCMDBSZ);
     narg=itemcntd(argstr," ");
     for (i=1,cp=argstr ; cp != NULL && *cp != '\0' && i <= narg ; i++) {
          argarr[i]=skpwht(cp);
          if ((cp=strchr(cp,' ')) != NULL) {
               *cp++='\0';
          }
     }
     argarr[i]=NULL;
     if ((i=spawnv(P_WAIT,cvtinf.cstcvt,argarr)) != 0) {
          catastro("SPAWNCVT: Error launching %s (error %d)!",cvtinf.cstcvt,i);
     }
#endif
#ifdef GCWINNT
     STARTUPINFO si;
     PROCESS_INFORMATION pi;
     CHAR cmdbuf[260];
     DWORD procrc;

     dfcDisplayInfo(NULL,NULL,NULL,0);

     memset(&si,0,sizeof(STARTUPINFO));
     si.cb=sizeof(STARTUPINFO);
     si.dwFlags=STARTF_USESTDHANDLES;
     sprintf(cmdbuf,"%s %s",cvtinf.cstcvt,cvtinf.cstcmdbuf);
     if (CreateProcess(cvtinf.cstcvt,cmdbuf,NULL,NULL,FALSE,
                       CREATE_DEFAULT_ERROR_MODE,NULL,NULL,&si,&pi)) {
          WaitForSingleObject(pi.hProcess,INFINITE);
          GetExitCodeProcess(pi.hProcess,(LPDWORD)&procrc);
          CloseHandle(pi.hProcess);
          CloseHandle(pi.hThread);
          if (procrc != 0) {
               catastro("SPAWNCVT: Error launching %s (error %d)!",
                        cvtinf.cstcvt,(INT)procrc);
          }
     }
     else {
          catastro("SPAWNCVT: CreateProcess error launching %s (error %d)!",
                   cvtinf.cstcvt,(INT)GetLastError());
     }
#endif
}

static VOID
getmfinfo(                         /* read info for mult src formats       */
CHAR *cvtfil)                      /*   conversion description file        */
{
     CHAR wrkbuf[MAXLSZ+1],*bufptr,bytval;
     UINT nbytrd;
     GBOOL foundlin;

     if ((cvtfp=fopen(cvtfil,FOPRA)) == NULL) {
          catastro("GETMFINFO: Error opening %s!",cvtfil);
     }
     fndlin("MultiFormat",cvtfp,cvtfil);
     // XXXXXXXXXX: xx xx xx... + \0
     foundlin=FALSE;
     while (dfgtstg(wrkbuf,MAXLSZ+1,cvtfp)) {
          if (sameas(wrkbuf,"EndMultiFormat")) {
               foundlin=TRUE;
               break;
          }
          cvtinf.srcfmts=(struct fmtinfo *)alcrsz(cvtinf.srcfmts,
                                                  sizeof(struct fmtinfo)*
                                                  cvtinf.nfmts,
                                                  sizeof(struct fmtinfo)*
                                                  (cvtinf.nfmts+1));
          setmem(&cvtinf.srcfmts[cvtinf.nfmts],sizeof(struct fmtinfo),0);
          cvtinf.dstfmts=(struct fmtinfo *)alcrsz(cvtinf.dstfmts,
                                                  sizeof(struct fmtinfo)*
                                                  cvtinf.nfmts,
                                                  sizeof(struct fmtinfo)*
                                                  (cvtinf.nfmts+1));
          setmem(&cvtinf.dstfmts[cvtinf.nfmts],sizeof(struct fmtinfo),0);
          cvtinf.fmtibufs=(CHAR **)alcrsz(cvtinf.fmtibufs,
                                          sizeof(CHAR *)*cvtinf.nfmts,
                                          sizeof(CHAR *)*(cvtinf.nfmts+1));

          bufptr=strtok(wrkbuf,":");
          stlcpy(cvtinf.srcfmts[cvtinf.nfmts].fmttag,bufptr,FLDTAGSIZ+1);
          stlcpy(cvtinf.dstfmts[cvtinf.nfmts].fmttag,bufptr,FLDTAGSIZ+1);
          cvtinf.fmtibufs[cvtinf.nfmts]=alcmem(cvtinf.indbuflen);
          nbytrd=0;
          while ((bufptr=strtok(NULL," ")) != NULL) {
               sscanf(bufptr,"%X",&bytval);
               cvtinf.fmtibufs[cvtinf.nfmts][nbytrd]=bytval;
               nbytrd++;
          }
          if (nbytrd != cvtinf.indbuflen) {
               catastro("GETMFINFO: Incorrect number of bytes specified for "
                        "tag \"%s\"!",cvtinf.srcfmts[cvtinf.nfmts].fmttag);
          }
          cvtinf.nfmts++;
     }
     fclose(cvtfp);
     if (!foundlin) {
          catastro("GETMFINFO: Didn't find \"EndMultiFormat\" tag in %s!",
                   cvtfil);
     }
}

static VOID
rdformat(                          /* read format information              */
struct fmtinfo *fmtptr,            /*   destination for format info        */
CHAR *cvtfil,                      /*   conversion description file        */
USHORT fmttype)                    /*   type of format to read             */
{
     CHAR wrkbuf[MAXLSZ+1],endlin[16];
     GBOOL foundlin=FALSE,gotvarlen=FALSE;
     USHORT curoffset,i,FDAoffset;
     struct fldinfo *fldptr;

     if ((cvtfp=fopen(cvtfil,FOPRA)) == NULL) {
          catastro("RDFORMAT: Error opening %s!",cvtfil);
     }

     // position file pointer to correct line
     fndlin((fmttype == SOURCE ? "SourceLayout" : "DestLayout"),cvtfp,cvtfil);
     if (fmtptr->fmttag[0] != '\0') {
          fndlin(fmtptr->fmttag,cvtfp,cvtfil);
          sprintf(endlin,"End%s",fmtptr->fmttag);
     }
     else {
          strcpy(endlin,fmttype == SOURCE ? "EndSourceLayout"
                                          : "EndDestLayout");
     }
     // set up starting offset
     switch (cvtinf.cvttype) {
     case DFC_NORMAL:
          curoffset=0;
          break;
     case DFC_GENDB:
          // add generic database fields, if doing generic conversion
          fmtptr->fields=(struct fldinfo *)alczer(sizeof(struct fldinfo)*2);
          strcpy(fmtptr->fields[0].fldtag,"GENDB_UID");
          fmtptr->fields[0].fldtyp=T_CHAR;
          if (fmttype == SOURCE) {
               fmtptr->fields[0].srcoffset=0;
          }
          else {
               fmtptr->fields[0].dstoffset=0;
          }
          fmtptr->fields[0].fldlen=UIDSIZ;
          fmtptr->fields[0].nelems=1;
          strcpy(fmtptr->fields[1].fldtag,"GENDB_MOD");
          fmtptr->fields[1].fldtyp=T_CHAR;
          if (fmttype == SOURCE) {
               fmtptr->fields[1].srcoffset=UIDSIZ;
          }
          else {
               fmtptr->fields[1].dstoffset=UIDSIZ;
          }
          fmtptr->fields[1].fldlen=MNMSIZ;
          fmtptr->fields[1].nelems=1;
          fmtptr->nflds=2;
          curoffset=GDBDOFF;
     }

     // read in field info, and add to format
     while (dfgtstg(wrkbuf,MAXLSZ+1,cvtfp)) {
          if (sameas(wrkbuf,endlin)) {
               foundlin=TRUE;
               break;
          }
          if (gotvarlen) {
               if (fmtptr->fmttag[0] == '\0') {
                    sprintf(wrkbuf,"RDFORMAT: Error reading %s format in"
                            "\n%s!\nVariable-length fields must be last "
                            "in the format!",
                            fmttype == SOURCE ? "source" : "destination",
                            cvtfil);
               }
               else {
                    sprintf(wrkbuf,"RDFORMAT: Error reading %s format %s in"
                            "\n%s!\nVariable-length fields must be last "
                            "in the format!",
                            fmttype == SOURCE ? "source" : "destination",
                            fmtptr->fmttag,cvtfil);
               }
               catastro(wrkbuf);

          }
          fmtptr->fields=alcrsz(fmtptr->fields,sizeof(struct fldinfo)*
                                               fmtptr->nflds,
                                               sizeof(struct fldinfo)*
                                               (fmtptr->nflds+1));
          fldptr=&fmtptr->fields[fmtptr->nflds];
          setmem(fldptr,sizeof(struct fldinfo),0);
          if (fmttype == SOURCE) {
               gtsrcfld(wrkbuf,fldptr,&curoffset);
          }
          else {
               gtdstfld(wrkbuf,fldptr,&curoffset,fmtptr);
          }
          if (fldptr->varlen) {
               gotvarlen=cvtinf.variable=TRUE;
          }
          fmtptr->nflds++;
     }
     fclose(cvtfp);
     if (!foundlin) {
          catastro("RDFORMAT: Didn't find \"%s\" tag in %s!",endlin,cvtfil);
     }

     // build the FDA for destination formats
     if (fmttype == DEST) {
          fmtptr->fmtFDA=(struct flddef *)alczer(sizeof(struct flddef)*
                                                 (fmtptr->nflds+1));
          for (i=0,fldptr=fmtptr->fields ; i < fmtptr->nflds ; i++,fldptr++) {
               fmtptr->fmtFDA[i].type=dfldinf[fldptr->fldtyp].cvttyp;
               if (fldptr->fldtyp == T_CHAR || fldptr->fldtyp == T_OPAQUE) {
                    fmtptr->fmtFDA[i].nelem=fldptr->fldlen;
               }
               else if (!fldptr->varlen) {
                    fmtptr->fmtFDA[i].nelem=fldptr->nelems;
               } // leave nelem 0 if var-len field

               if (cvtinf.dstpacked || fldptr->fldtyp == T_CHAR
                || fldptr->fldtyp == T_OPAQUE) {
                    fmtptr->fmtFDA[i].offset=fldptr->dstoffset;
               }
               else { // FDAs must always describe data as unpacked
                    FDAoffset=fldptr->dstoffset;
                    if (fldptr->fldtyp != T_STRUCT) {
                         adjoff(&FDAoffset,fldptr->fldlen);
                    }
                    else {
                         adjoff(&FDAoffset,fldptr->largest);
                    }
                    fmtptr->fmtFDA[i].offset=FDAoffset;
               }
               if (fldptr->fldtyp == T_STRUCT) {
                    fmtptr->fmtFDA[i].substruct=fldptr->subFDA;
               }
          }
     }
}

static VOID
gtsrcfld(                          /* get source field info                */
CHAR *finfstg,                     /*   line from conversion desc file     */
struct fldinfo *sfldptr,           /*   dest for source field info         */
USHORT *curoffset)                 /*   current offset in cvtinf.srcbuf    */
{
     CHAR wrkbuf[MAXLSZ+1],*begptr,*endptr;
     INT typidx;

     begptr=skpwht(finfstg);
     endptr=skpwrd(begptr);
     stlcpy(sfldptr->fldtag,begptr,((INT)(endptr-begptr))+1);
     getfinf(finfstg,"TYPE",NILDELIM,wrkbuf,TRUE);
     if ((typidx=fnddfld(wrkbuf)) == -1) {
          catastro("RDSRCFLD: Unknown field type \"%s\" in %s!",wrkbuf,finfstg);
     }
     sfldptr->nelems=1;
     switch (sfldptr->fldtyp=dfldinf[typidx].fldtyp) {
     case T_CHAR:
     case T_OPAQUE:
          sfldptr->varlen=FALSE;
          getfinf(finfstg,"LENGTH",NILDELIM,wrkbuf,TRUE);
          if (sameas(wrkbuf,"VARIABLE")) {
               sfldptr->varlen=TRUE;
               getfinf(finfstg,"MAXLENGTH",NILDELIM,wrkbuf,TRUE);
          }
          sfldptr->fldlen=atoi(wrkbuf);
          break;
     case T_STRUCT:
          getfinf(finfstg,"MEMBERS",NILDELIM,wrkbuf,TRUE);
          sfldptr->nmembers=atoi(wrkbuf);
          sfldptr->fldlen=getmembers(sfldptr,SOURCE);
          sfldptr->largest=largestfsz(sfldptr,FLD);
          if (!cvtinf.srcpacked) {
               adjoff(curoffset,sfldptr->largest);
          }
          sfldptr->fldlen*=sfldptr->nelems;
          break;
     default:
          if (getfinf(finfstg,"NUMELEMS",NILDELIM,wrkbuf,FALSE)) {
               sfldptr->nelems=atoi(wrkbuf);
          }
          sfldptr->fldlen=dfldinf[typidx].fldlen;
          if (getfinf(finfstg,"MAXELEMS",NILDELIM,wrkbuf,FALSE)) {
               sfldptr->varlen=TRUE;
               sfldptr->nelems=atoi(wrkbuf);
          }
          if (!cvtinf.srcpacked) {
               adjoff(curoffset,sfldptr->fldlen);
          }
     }
     sfldptr->srcoffset=*curoffset;
     *curoffset+=sfldptr->fldlen*sfldptr->nelems;
}

static USHORT
getmembers(                        /* get field info for STRUCT field      */
struct fldinfo *pfldptr,           /*   field to get members for           */
USHORT srcdst)                     /*   SOURCE or DEST field               */
{
     USHORT curoff=0,i,FDAoffset;
     INT typidx;
     CHAR finfstg[MAXLSZ+1],wrkbuf[MAXLSZ+1],*begptr,*endptr;
     struct fldinfo *memfptr;

     pfldptr->members=(struct fldinfo *)alczer(sizeof(struct fldinfo)*
                                               pfldptr->nmembers);
     for (i=0 ; i < pfldptr->nmembers ; i++) {
          memfptr=&pfldptr->members[i];
          dfgtstg(finfstg,MAXLSZ+1,cvtfp);
          begptr=skpwht(finfstg);
          endptr=skpwrd(begptr);
          stlcpy(memfptr->fldtag,begptr,((INT)(endptr-begptr))+1);
          getfinf(finfstg,"TYPE",NILDELIM,wrkbuf,TRUE);
          if ((typidx=fnddfld(wrkbuf)) == -1) {
               catastro("GETMEMBERS: Unknown field type \"%s\" in %s!",
                        wrkbuf,finfstg);
          }
          memfptr->nelems=1;
          if (getfinf(finfstg,"NUMELEMS",NILDELIM,wrkbuf,FALSE)) {
               memfptr->nelems=atoi(wrkbuf);
          }
          switch (memfptr->fldtyp=dfldinf[typidx].fldtyp) {
          case T_OPAQUE:
               if (srcdst == DEST) {
                    catastro("GETMEMBERS: OPAQUE fields not allowed in "
                             "destination subfields: %s",finfstg);
               }
          case T_CHAR:
               getfinf(finfstg,"LENGTH",NILDELIM,wrkbuf,TRUE);
               if (sameas(wrkbuf,"VARIABLE")) {
                    catastro("GETMEMBERS: VARIABLE Subfields not allowed:\n%s",
                             finfstg);
               }
               memfptr->fldlen=atoi(wrkbuf);
               break;
          case T_STRUCT:
               getfinf(finfstg,"MEMBERS",NILDELIM,wrkbuf,TRUE);
               memfptr->nmembers=atoi(wrkbuf);
               memfptr->fldlen=getmembers(memfptr,srcdst);
               memfptr->largest=largestfsz(memfptr,FLD);
               if (!cvtinf.srcpacked) {
                    adjoff(&curoff,memfptr->largest);
               }
               memfptr->fldlen*=memfptr->nelems;
               break;
          default:
               memfptr->fldlen=dfldinf[typidx].fldlen;
               if (!cvtinf.srcpacked) {
                    adjoff(&curoff,memfptr->fldlen);
               }
               memfptr->fldlen*=memfptr->nelems;
          }
          if (srcdst == SOURCE) {
               memfptr->srcoffset=curoff;
          }
          else {
               memfptr->dstoffset=curoff;
          }
          curoff+=memfptr->fldlen;
     }
     if (srcdst == DEST) {
          pfldptr->subFDA=(struct flddef *)alczer(sizeof(struct flddef)*
                                                  (pfldptr->nmembers+1));
          for (i=0,memfptr=pfldptr->members ; i < pfldptr->nmembers
                                            ; i++,memfptr++) {
               pfldptr->subFDA[i].type=dfldinf[memfptr->fldtyp].cvttyp;
               if (memfptr->fldtyp == T_CHAR || memfptr->fldtyp == T_OPAQUE) {
                    pfldptr->subFDA[i].nelem=memfptr->fldlen;
               }
               else {
                    pfldptr->subFDA[i].nelem=memfptr->nelems;
               }
               if (cvtinf.dstpacked || memfptr->fldtyp == T_CHAR
                || memfptr->fldtyp == T_OPAQUE) {
                    pfldptr->subFDA[i].offset=memfptr->dstoffset;
               }
               else { // FDAs must always describe data as unpacked
                    FDAoffset=memfptr->dstoffset;
                    if (memfptr->fldtyp != T_STRUCT) {
                         adjoff(&FDAoffset,memfptr->fldlen);
                    }
                    else {
                         adjoff(&FDAoffset,memfptr->largest);
                    }
                    pfldptr->subFDA[i].offset=FDAoffset;
               }
               if (memfptr->fldtyp == T_STRUCT) {
                    pfldptr->subFDA[i].substruct=memfptr->subFDA;
               }
          }
     }
     return(curoff);
}

static VOID
gtdstfld(                          /* get destination field info           */
CHAR *finfstg,                     /*   line from conversion desc file     */
struct fldinfo *dfldptr,           /*   dest for dest field info           */
USHORT *curoffset,                 /*   current offset in cvtinf.dstbuf    */
struct fmtinfo *dstfmt)            /*   dest format info                   */
{
     CHAR *begptr,*endptr;
     struct fmtinfo *sfmtptr;
     struct fldinfo *sfldptr;
     USHORT i,len;

     begptr=skpwht(finfstg);
     endptr=skpwrd(begptr);
     stlcpy(dfldptr->fldtag,begptr,((INT)(endptr-begptr))+1);
     sfmtptr=NULL;
     if (dstfmt->fmttag[0] == '\0') {
          sfmtptr=cvtinf.srcfmts;
     }
     else {
          for (i=0 ; i < cvtinf.nfmts ; i++) {
               if (sameas(cvtinf.srcfmts[i].fmttag,dstfmt->fmttag)) {
                    sfmtptr=&cvtinf.srcfmts[i];
                    break;
               }
          }
     }
     if (sfmtptr == NULL) {
          catastro("RDDSTFLD: unable to find match for \"%s\" in source "
                   "formats!",dstfmt->fmttag);
     }
     if ((sfldptr=fndfld(sfmtptr,dfldptr->fldtag)) == NULL) {
          supnewdfld(finfstg,dfldptr);
     }
     else {
          supxstdfld(finfstg,dfldptr,sfldptr);
     }
     if (!cvtinf.dstpacked) {
          if (dfldptr->fldtyp == T_STRUCT) {
               adjoff(curoffset,dfldptr->largest);
          }
          else if (dfldptr->fldtyp != T_CHAR && dfldptr->fldtyp != T_OPAQUE) {
               adjoff(curoffset,dfldinf[dfldptr->fldtyp].fldlen);
          }
     }
     dfldptr->dstoffset=*curoffset;
     if (dfldptr->fldtyp != T_STRUCT) {
          *curoffset+=dfldptr->fldlen*dfldptr->nelems;
     }
     else { // need to account for structure padding
          len=dfldptr->fldlen;
          adjoff(&len,dfldptr->largest);
          *curoffset+=len*dfldptr->nelems;
     }
}

static VOID
supnewdfld(                        /* set up new dest fld (not in src fmt) */
CHAR *finfstg,                     /*   line from conversion desc file     */
struct fldinfo *dfldptr)           /*   dest for dest field info           */
{
     CHAR wrkbuf[30],delim;
     INT typidx;

     getfinf(finfstg,"TYPE",NILDELIM,wrkbuf,TRUE);
     if ((typidx=fnddfld(wrkbuf)) == -1) {
          catastro("RDDSTFLD: Unknown field type \"%s\" in %s!",wrkbuf,finfstg);
     }
     dfldptr->nelems=1;
     switch (dfldptr->fldtyp=dfldinf[typidx].fldtyp) {
     case T_OPAQUE:
          catastro("RDDSTFLD: OPAQUE fields not allowed in destination "
                   "formats: %s",finfstg);
     case T_CHAR:
          dfldptr->varlen=FALSE;
          getfinf(finfstg,"LENGTH",NILDELIM,wrkbuf,TRUE);
          if (sameas(wrkbuf,"VARIABLE")) {
               dfldptr->varlen=TRUE;
               getfinf(finfstg,"MAXLENGTH",NILDELIM,wrkbuf,TRUE);
          }
          dfldptr->fldlen=atoi(wrkbuf);
          delim='\"';
          break;
     case T_STRUCT:
          getfinf(finfstg,"MEMBERS",NILDELIM,wrkbuf,TRUE);
          dfldptr->nmembers=atoi(wrkbuf);
          dfldptr->fldlen=getmembers(dfldptr,DEST);
          dfldptr->largest=largestfsz(dfldptr,FLD);
          dfldptr->fldlen*=dfldptr->nelems;
          break;
     default:
          if (getfinf(finfstg,"NUMELEMS",NILDELIM,wrkbuf,FALSE)) {
               dfldptr->nelems=atoi(wrkbuf);
          }
          dfldptr->fldlen=dfldinf[typidx].fldlen;
          if (getfinf(finfstg,"MAXELEMS",NILDELIM,wrkbuf,FALSE)) {
               dfldptr->varlen=TRUE;
               dfldptr->nelems=atoi(wrkbuf);
          }
          delim=NILDELIM;
     }
     dfldptr->defval=(VOID *)alcmem(dfldptr->fldlen);
     if (getfinf(finfstg,"DEFAULT",delim,wrkbuf,FALSE)) {
          setdefval(dfldptr,wrkbuf);
     }
     else {
          setmem(dfldptr->defval,dfldptr->fldlen,0);
     }
}

static VOID
setdefval(                         /* set default value for new dest field */
struct fldinfo *dfldptr,           /*   dest field info                    */
CHAR *valbuf)                      /*   default value data                 */
{
     CHAR stgdat[11],stgtim[9];

     switch (dfldptr->fldtyp) {
     case T_CHAR:
          stlcpy((CHAR *)dfldptr->defval,valbuf,strlen(valbuf)+1);
          break;
     case T_SHORT:
          sscanf(valbuf,"%d",(SHORT *)dfldptr->defval);
          break;
     case T_LONG:
          sscanf(valbuf,"%ld",(LONG *)dfldptr->defval);
          break;
     case T_FLOAT:
          sscanf(valbuf,"%f",(FLOAT *)dfldptr->defval);
          break;
     case T_DOUBLE:
          sscanf(valbuf,"%lf",(DOUBLE *)dfldptr->defval);
          break;
     case T_VBDATE:
          strcpy(stgdat,itemidxd(valbuf,0,";"));
          strcpy(stgtim,itemidxd(valbuf,1,";"));
          *((DOUBLE *)dfldptr->defval)=s2vdat(stgdat,stgtim);
          break;
     case T_DDATE:
          *((USHORT *)dfldptr->defval)=dcdate(valbuf);
          break;
     case T_DTIME:
          *((USHORT *)dfldptr->defval)=dctime(valbuf);
          break;
     default:
          catastro("SETDEFVAL: Unknown data type %d in in field \"%s\"!",
                   dfldptr->fldtyp,dfldptr->fldtag);
     }
}

static VOID
supxstdfld(                        /* set up existing dest fld             */
CHAR *finfstg,                     /*   line from conversion desc file     */
struct fldinfo *dfldptr,           /*   dest for dest field info           */
struct fldinfo *sfldptr)           /*   cooresponding source field info    */
{
     CHAR wrkbuf[MAXLSZ+1];
     USHORT i,curoff;
     struct fldinfo *smemfptr,*dmemfptr;

     dfldptr->fldtyp=sfldptr->fldtyp;
     dfldptr->varlen=sfldptr->varlen;
     dfldptr->nelems=sfldptr->nelems;
     if (dfldptr->fldtyp == T_STRUCT) {
          dfldptr->nmembers=sfldptr->nmembers;
          dfldptr->largest=sfldptr->largest;
          dfldptr->members=(struct fldinfo *)alczer(sizeof(struct fldinfo)*
                                                    sfldptr->nmembers);
          movmem(sfldptr->members,dfldptr->members,sizeof(struct fldinfo)*
                                                   sfldptr->nmembers);
          curoff=0;
          smemfptr=sfldptr->members;
          dmemfptr=dfldptr->members;
          for (i=0 ; i < dfldptr->nmembers ; i++,smemfptr++,dmemfptr++) {
               dmemfptr->srcoffset=0; // blank out - unneeded
               if (cvtinf.srcpacked && !cvtinf.dstpacked
                && dmemfptr->fldtyp != T_CHAR && dmemfptr->fldtyp != T_OPAQUE) {
                    if (dmemfptr->fldtyp != T_STRUCT) {
                         adjoff(&curoff,dmemfptr->fldlen);
                    }
                    else {
                         adjoff(&curoff,dmemfptr->largest);
                    }
               }
               dmemfptr->dstoffset=curoff;
               curoff+=dmemfptr->fldlen;
          }
          dfldptr->fldlen=curoff;

          dfldptr->subFDA=(struct flddef *)alczer(sizeof(struct flddef)*
                                                  (dfldptr->nmembers+1));
          for (i=0,dmemfptr=dfldptr->members ; i < dfldptr->nmembers
                                             ; i++,dmemfptr++) {
               dfldptr->subFDA[i].type=dfldinf[dmemfptr->fldtyp].cvttyp;
               if (dmemfptr->fldtyp == T_CHAR || dmemfptr->fldtyp == T_OPAQUE) {
                    dfldptr->subFDA[i].nelem=dmemfptr->fldlen;
               }
               else {
                    dfldptr->subFDA[i].nelem=dmemfptr->nelems;
               }
               dfldptr->subFDA[i].offset=dmemfptr->dstoffset;
          }
     }
     else if (dfldptr->fldtyp != T_CHAR && dfldptr->fldtyp != T_OPAQUE) {
          dfldptr->fldlen=sfldptr->fldlen;
          if (dfldptr->varlen && getfinf(finfstg,"MAXCOPY",NILDELIM,wrkbuf,
                                         FALSE)) {
               dfldptr->maxcopy=atoi(wrkbuf);
          }
     }
     else if (dfldptr->varlen) {
          supvlcodfld(finfstg,dfldptr,sfldptr);
     }
     else {
          supcodfld(finfstg,dfldptr,sfldptr);
     }
}

static VOID
supvlcodfld(          /* set up existing var-len CHAR or OPAQUE dest field */
CHAR *finfstg,                     /*   line from conversion desc file     */
struct fldinfo *dfldptr,           /*   dest for dest field info           */
struct fldinfo *sfldptr)           /*   cooresponding source field info    */
{
     GBOOL gotmaxlen,gotmaxcpy;
     CHAR maxlenbuf[11],maxcpybuf[11],wrkbuf[30];

     gotmaxlen=getfinf(finfstg,"MAXLENGTH",NILDELIM,maxlenbuf,FALSE);
     gotmaxcpy=getfinf(finfstg,"MAXCOPY",NILDELIM,maxcpybuf,FALSE);
     if (gotmaxlen && gotmaxcpy) {
          dfldptr->fldlen=atoi(maxlenbuf);
          dfldptr->maxcopy=atoi(maxcpybuf);
          if (dfldptr->maxcopy > dfldptr->fldlen) {
               catastro("RDDSTFLD: field %s - MAXCOPY (%d) is more than "
                        "MAXLENGTH (%d)!",dfldptr->fldtag,dfldptr->maxcopy,
                         dfldptr->fldlen);
          }
          if (dfldptr->maxcopy > sfldptr->fldlen) {
               catastro("RDDSTFLD: field %s - MAXCOPY (%d) is more than "
                        "length of source field (%d)",dfldptr->fldtag,
                        dfldptr->maxcopy,sfldptr->fldlen);
          }
          if (dfldptr->fldlen > sfldptr->fldlen) {
               dfldptr->justify=LEFT;
          }
          else if (dfldptr->fldlen < sfldptr->fldlen) {
               dfldptr->truncate=RIGHT;
          }
          if (getfinf(finfstg,"PAD",NILDELIM,wrkbuf,FALSE)) {
               sscanf(wrkbuf,"%X",&dfldptr->pad);
          }
     }
     else if (gotmaxlen) {
          dfldptr->fldlen=atoi(maxlenbuf);
          if (dfldptr->fldlen > sfldptr->fldlen) {
               dfldptr->justify=LEFT;
          }
          else if (dfldptr->fldlen < sfldptr->fldlen) {
               dfldptr->truncate=RIGHT;
          }
          if (getfinf(finfstg,"PAD",NILDELIM,wrkbuf,FALSE)) {
               sscanf(wrkbuf,"%X",&dfldptr->pad);
          }
     }
     else if (gotmaxcpy) {
          dfldptr->maxcopy=atoi(maxcpybuf);
          if (dfldptr->maxcopy > sfldptr->fldlen) {
               catastro("RDDSTFLD: field %s - MAXCOPY (%d) is more than "
                        "length of source field (%d)",dfldptr->fldtag,
                        dfldptr->maxcopy,sfldptr->fldlen);
          }
          dfldptr->fldlen=sfldptr->fldlen;
          dfldptr->justify=LEFT;
          if (getfinf(finfstg,"PAD",NILDELIM,wrkbuf,FALSE)) {
               sscanf(wrkbuf,"%X",&dfldptr->pad);
          }
     }
     else {
          dfldptr->fldlen=sfldptr->fldlen;
     }
}

static VOID
supcodfld(                         /* set up CHAR or OPAQUE dest field     */
CHAR *finfstg,                     /*   line from conversion desc file     */
struct fldinfo *dfldptr,           /*   dest for dest field info           */
struct fldinfo *sfldptr)           /*   cooresponding source field info    */
{
     CHAR wrkbuf[30];

     dfldptr->varlen=FALSE;
     if (!getfinf(finfstg,"LENGTH",NILDELIM,wrkbuf,FALSE)) {
          dfldptr->fldlen=sfldptr->fldlen;
     }
     else {
          dfldptr->fldlen=atoi(wrkbuf);
          if (dfldptr->fldlen > sfldptr->fldlen) {
               getfinf(finfstg,"JUSTIFY",NILDELIM,wrkbuf,TRUE);
               dfldptr->justify=(sameas(wrkbuf,"LEFT") ? LEFT : RIGHT);
               if (getfinf(finfstg,"PAD",NILDELIM,wrkbuf,FALSE)) {
                    sscanf(wrkbuf,"%X",&dfldptr->pad);
               }
          }
          else if (dfldptr->fldlen < sfldptr->fldlen) {
               getfinf(finfstg,"TRUNCATE",NILDELIM,wrkbuf,TRUE);
               dfldptr->truncate=(sameas(wrkbuf,"LEFT") ? LEFT : RIGHT);
          }
     }
}

static GBOOL
supgupdinf(                        /* set up convert info for generic db   */
CHAR *cvtfil)                      /*   conversion description file        */
{
     GBOOL rc=FALSE;
     CHAR *cpModule;
#ifdef GCWINNT
     CHAR *upgradetype;
#endif // GCWINNT

     readcvt("Description:",cvtinf.cvtdsc,cvtfil,FALSE);
     cvtinf.cvttype=DFC_GENDB;
#ifdef GCWINNT
     upgradetype=getUpdType();
     if (sameas(upgradetype,"2.0 to NT")) {
          stlcpy(cvtinf.srcdatfil,"BBSGEN.D_T",GCMAXFNM);
          cvtinf.srcpacked=TRUE;
          cvtinf.srcbytord=LITTLE;
          stlcpy(cvtinf.dstdatfil,"WGSGEN2.DAT",GCMAXFNM);
          cvtinf.dstpacked=FALSE;
          cvtinf.dstbytord=LITTLE;
     }
     else {
          stlcpy(cvtinf.srcdatfil,"WGSGEN2.D_T",GCMAXFNM);
          cvtinf.srcpacked=TRUE;
          cvtinf.srcbytord=LITTLE;
          stlcpy(cvtinf.dstdatfil,"WGSGEN2.DAT",GCMAXFNM);
          cvtinf.dstpacked=FALSE;
          cvtinf.dstbytord=LITTLE;
     }
#endif // GCWINNT
#ifdef GCDOS
     stlcpy(cvtinf.srcdatfil,"BBSGEN.D_T",GCMAXFNM);
     cvtinf.srcpacked=TRUE;
     cvtinf.srcbytord=LITTLE;
     stlcpy(cvtinf.dstdatfil,"WGSGEN2.DAT",GCMAXFNM);
     cvtinf.dstpacked=TRUE;
     cvtinf.dstbytord=LITTLE;
#endif // GCDOS
     cvtinf.recorder=KEY;
     cvtinf.variable=TRUE;
     cvtinf.ratio=GENRATIO;
     if (!readcvt("CustomConvert:",cvtinf.cstcvt,cvtfil,FALSE)) {
          readcvt("ModuleName:",cvtinf.gendbkey.module,cvtfil,TRUE);
          if (readcvt("Userid:",cvtinf.gendbkey.userid,cvtfil,FALSE)) {
               cvtinf.keyno=0;
          }
          else {
               cvtinf.keyno=1;
          }
          if (readcvt("RunAfterConvert:",cvtinf.cstcvt,cvtfil,FALSE)) {
               sprintf(cvtinf.cstcmdbuf,
                       "keep %s %s %s %s %s %s key,%d %1.1f \"%s\"",
                       cvtinf.srcdatfil,
                       cvtinf.srcpacked ? "yes" : "no",
                       cvtinf.srcbytord == LITTLE ? "little" : "big",
                       cvtinf.dstdatfil,
                       cvtinf.dstpacked ? "yes" : "no",
                       cvtinf.dstbytord == LITTLE ? "little" : "big",
                       cvtinf.keyno,
                       cvtinf.ratio,
                       cvtinf.gendbkey.module);
               if (cvtinf.keyno == 0) {
                    strcat(cvtinf.cstcmdbuf,spr(" \"%s\"",
                                                cvtinf.gendbkey.userid));
               }
               if (dbgcvt) {
                    strcat(cvtinf.cstcmdbuf," debug");
               }
          }
     }
     else {
          if (readcvt("ModuleName:",cvtinf.gendbkey.module,cvtfil,FALSE)) {
               cpModule=cvtinf.gendbkey.module;
          }
          else {
               cpModule="?";
          }
          if (readcvt("Userid:",cvtinf.gendbkey.userid,cvtfil,FALSE)) {
               cvtinf.keyno=0;
          }
          else {
               cvtinf.keyno=1;
          }
          sprintf(cvtinf.cstcmdbuf,
                  "keep %s %s %s %s %s %s key,%d %1.1f \"%s\"",
                  cvtinf.srcdatfil,
                  cvtinf.srcpacked ? "yes" : "no",
                  cvtinf.srcbytord == LITTLE ? "little" : "big",
                  cvtinf.dstdatfil,
                  cvtinf.dstpacked ? "yes" : "no",
                  cvtinf.dstbytord == LITTLE ? "little" : "big",
                  cvtinf.keyno,
                  cvtinf.ratio,
                  cpModule);
          if (cvtinf.keyno == 0) {
               strcat(cvtinf.cstcmdbuf,spr(" \"%s\"",cvtinf.gendbkey.userid));
          }
          if (dbgcvt) {
               strcat(cvtinf.cstcmdbuf," debug");
          }
          rc=TRUE;
     }
     return(rc);
}

static VOID
supbufs(VOID)               /* set up src, dest and convert bufs in cvtinf */
{
     USHORT largest,i,buflen,curoffset=0;
     struct fmtinfo *sfmtptr,*dfmtptr;
     struct fldinfo *dfldptr,*lstfptr;

     sfmtptr=cvtinf.srcfmts;
     dfmtptr=cvtinf.dstfmts;

     lstfptr=&sfmtptr->fields[sfmtptr->nflds-1];
     buflen=lstfptr->srcoffset+(lstfptr->fldlen*lstfptr->nelems);
     if (!cvtinf.srcpacked && (largest=largestfsz(sfmtptr,FMT)) > 0) {
          adjoff(&buflen,largest);
     }
     cvtinf.srcbuf=alczer(cvtinf.srcbuflen=buflen);

     lstfptr=&dfmtptr->fields[dfmtptr->nflds-1];
     buflen=lstfptr->dstoffset+(lstfptr->fldlen*lstfptr->nelems);
     if (!cvtinf.dstpacked && (largest=largestfsz(dfmtptr,FMT)) > 0) {
          adjoff(&buflen,largest);
     }
     cvtinf.dstbuf=alczer(cvtinf.dstbuflen=buflen);

     if (cvtinf.srcpacked) {
          for (i=0,dfldptr=dfmtptr->fields ; i < dfmtptr->nflds
                                                              ; i++,dfldptr++) {
               dfldptr->cvtoffset=curoffset;
               curoffset+=dfldptr->fldlen*dfldptr->nelems;
          }
     }
     else {
          for (i=0,dfldptr=dfmtptr->fields ; i < dfmtptr->nflds
                                                              ; i++,dfldptr++) {
               if (dfldptr->fldtyp == T_STRUCT) {
                    adjoff(&curoffset,dfldptr->largest);
               }
               else if (dfldptr->fldtyp != T_CHAR
                     && dfldptr->fldtyp != T_OPAQUE) {
                    adjoff(&curoffset,dfldptr->fldlen);
               }
               dfldptr->cvtoffset=curoffset;
               curoffset+=dfldptr->fldlen*dfldptr->nelems;
          }
     }
     lstfptr=&dfmtptr->fields[dfmtptr->nflds-1];
     buflen=lstfptr->cvtoffset+(lstfptr->fldlen*lstfptr->nelems);
     if (!cvtinf.srcpacked && (largest=largestfsz(dfmtptr,FMT)) > 0) {
          adjoff(&buflen,largest);
     }
     cvtinf.cvtbuf=alczer(cvtinf.cvtbuflen=buflen);
}

static USHORT                      /*   returns size of largest field type */
largestfsz(                        /* find largest field type size         */
VOID *structptr,                   /*   structure to search                */
USHORT what)                       /*   format or field structure?         */
{
     USHORT largest=0,i,cursiz,nflds;
     struct fldinfo *fldptr;

     if (what == FMT) {
          fldptr=((struct fmtinfo *)structptr)->fields;
          nflds=((struct fmtinfo *)structptr)->nflds;
     }
     else {
          fldptr=((struct fldinfo *)structptr)->members;
          nflds=((struct fldinfo *)structptr)->nmembers;
     }
     for (i=0 ; i < nflds ; i++,fldptr++) {
          if (fldptr->fldtyp != T_CHAR && fldptr->fldtyp != T_OPAQUE) {
               if (fldptr->fldtyp != T_STRUCT) {
                    cursiz=fldptr->fldlen;
               }
               else {
                    cursiz=fldptr->largest;
               }
               if (cursiz > largest) {
                    largest=cursiz;
               }
          }
     }
     return(largest);
}

static GBOOL                       /*   TRUE=found information             */
getfinf(                           /* get info from cvt desc file line     */
CHAR *finfstg,                     /*   line from conversion desc file     */
CHAR *inftag,                      /*   tag of info we want                */
CHAR delim,                        /*   info value delimiter (or NILDELIM) */
CHAR *infbuf,                      /*   dest for info value                */
GBOOL bomb)                        /*   catastro() if not present?         */
{
     CHAR *infptr,*begptr,*endptr;

     strupr(finfstg);
     strupr(inftag);
     if ((infptr=strstr(finfstg,inftag)) != NULL) {
          begptr=strchr(infptr,'=');
     }
     else {
          begptr=NULL;
     }
     if (infptr == NULL || begptr == NULL) {
          if (bomb) {
               catastro("GETFINF: Couldn't find \"%s\" info in %s!",
                        inftag,finfstg);
          }
          return(FALSE);
     }
     if (delim != '\0') {
          begptr=strchr(begptr,delim);
          endptr=strrchr(begptr,delim);
          if (begptr == NULL || endptr == NULL) {
               if (bomb) {
                    catastro("GETFINF: Value for \"%s\" tag in %s must be "
                             "enclosed in with '%c' characters!",inftag,
                             finfstg,delim);
               }
               return(FALSE);
          }
     }
     else {
          endptr=skpwrd(begptr);
     }
     stlcpy(infbuf,begptr+1,(INT)(endptr-begptr));
     return(TRUE);
}

static INT                         /*   returns index into dfldinf[], or -1*/
fnddfld(                           /* fnd default field info, by id stg    */
CHAR *idstg)                       /*   type id stg to search for          */
{
     INT i;

     for (i=0 ; i < NFTYP ; i++) {
          if (sameas(idstg,dfldinf[i].idstg)) {
               return(i);
          }
     }
     return(-1);
}

static VOID
adjoff(                            /* adj offset for proper byte alignment */
USHORT *curoffset,                 /*   offset to adjust                   */
USHORT boundval)                   /*   bounding value to adjust to        */
{
     if (boundval > MAXPADDING) {
          boundval=MAXPADDING;
     }
     while (*curoffset % boundval != 0) {
          (*curoffset)++;
     }
}

static VOID
fndlin(                            /* find (& pos to) line in cvt desc file*/
CHAR *srchline,                    /*   line to search for                 */
FILE *fp,                          /*   pointer to open file to search     */
CHAR *filnam)                      /*   name of file to search             */
{
     GBOOL found=FALSE;
     CHAR wrkbuf[MAXLSZ+1];

     while (dfgtstg(wrkbuf,MAXLSZ+1,fp)) {
          if (sameto(srchline,wrkbuf)) {
               found=TRUE;
               break;
          }
     }
     if (!found) {
          catastro("FNDLIN: Didn't find \"%s\" tag in %s!",srchline,filnam);
     }
}

INT                                /*   0=error or EOF, 1=got line ok      */
dfgtstg(                           /* read line from cvt desc file         */
CHAR *destbuf,                     /*   destination buffer for line        */
INT size,                          /*   size of destination buffer         */
FILE *cvtfp)                       /*   file pointer to open cvt desc file */
{
     CHAR *cp;
     CHAR buf[MAXLSZ+1];

     destbuf[0]='\0';
     while (destbuf[0] == '\0') {
          buf[0]='\0';
          if (fgets(buf,MAXLSZ+1,cvtfp) == NULL) {
               return(0);
          }
          if (*(cp=buf+strlen(buf)-1) == '\n') {
               *cp='\0';
          }
          stpcmt(buf);
          unpad(buf);
          stzcpy(destbuf,buf,size);
     }
     return(1);
}

static VOID
stpcmt(                            /* strip comment from line              */
CHAR *buf)                         /*   line to strip comment from         */
{
     CHAR *cp;

     ASSERT(buf != NULL);
     if ((cp=strchr(buf,';')) != NULL) {
          *cp='\0';
     }
}

static VOID
procdatfil(VOID)                   /* proccess (convert) a data file       */
                                   /*   implicit input: cvtinf             */
{
     CHAR wrkbuf[MAXLSZ+1],dstnam[GCMAXFNM],*wgdst,*cwd;
     GBOOL morerec;
     struct fmtinfo *sfmtptr,*dfmtptr;
     USHORT i;

     dfaMode(ACCLBV);
     chkSrcDstNams(cvtinf.srcdatfil,cvtinf.dstdatfil,&cvtinf.tmpsrcnam);
     if (cvtinf.tmpsrcnam != NULL
      && (cvtinf.cvttype == DFC_NORMAL || !isfile(cvtinf.tmpsrcnam))) {
          if (rename(cvtinf.srcdatfil,cvtinf.tmpsrcnam) != 0) {
               catastro("PROCDATFIL: Source and dest are same, unable to "
                         "rename %s to %s!",
                         cvtinf.srcdatfil,cvtinf.tmpsrcnam);
          }
     }
     if (cvtinf.cvttype == DFC_NORMAL) {
          fileparts(GCPART_FILE,cvtinf.dstdatfil,dstnam,sizeof(dstnam));
          if (!dfaVirgin(dstnam,dstnam)) {
               catastro("PROCDATFIL: Unable to create %s.DAT from %s.VIR!\n"
                        "(%s)",dstnam,dstnam,cvtinf.cvtdsc);
          }

          // handle case where dest data file is in directory other than
          // server root directory
          fileparts(GCPART_PATH,cvtinf.dstdatfil,wrkbuf,GCMAXPTH);
          if (wrkbuf[0] != '\0') {
               cwd=getcwd(NULL,GCMAXPTH);
               if (!sameas(wrkbuf,spr("%s\\",cwd))) {
                    wgdst=spr("%s\\%s.DAT",cwd,dstnam);
                    if (!movefile(wgdst,cvtinf.dstdatfil)) {
                         catastro("PROCDATFIL: Unable to move %s to %s!",
                                   wgdst,cvtinf.dstdatfil);
                    }
               }
               free(cwd);
          }
          if (cvtinf.tmpsrcnam == NULL) {
               srcbb=dfaOpen(cvtinf.srcdatfil,cvtinf.srcbuflen,NULL);
          }
          else {
               srcbb=dfaOpen(cvtinf.tmpsrcnam,cvtinf.srcbuflen,NULL);
          }
          dstbb=dfaOpen(cvtinf.dstdatfil,cvtinf.dstbuflen,NULL);
          while (dfaStepLO(NULL)) {  // avoid insert 5 errors on .VIR files
               dfaDelete();          // which ship w/ pre-inserted records
          }
     }
     else {
          if (!isfile(cvtinf.dstdatfil)) {
               fileparts(GCPART_FILE,cvtinf.dstdatfil,wrkbuf,GCMAXFILE+1);
               if (!dfaVirgin(wrkbuf,wrkbuf)) {
                    catastro("PROCDATFIL: Unable to find %s.VIR!\n"
                             "(%s)",wrkbuf,cvtinf.cvtdsc);
               }
          }
          if (cvtinf.tmpsrcnam == NULL) {
               srcbb=dfaOpen(cvtinf.srcdatfil,GENSIZ,NULL);
          }
          else {
               srcbb=dfaOpen(cvtinf.tmpsrcnam,GENSIZ,NULL);
          }
          dstbb=dfaOpen(cvtinf.dstdatfil,GENSIZ,NULL);
     }
     if (!ckrecshdrm()) {
          dfaClose(srcbb);
          srcbb=NULL;
          dfaClose(dstbb);
          dstbb=NULL;
          if (cvtinf.cvttype == DFC_NORMAL) {
               unlink(cvtinf.dstdatfil);
          }
          return;
     }
     if ((morerec=get1strec(srcbb)) == FALSE) {
          catastro("PROCDATFIL: Unable to read records from %s!\n"
                   "(%s)",cvtinf.srcdatfil,cvtinf.cvtdsc);
     }
     currec=0L;
     if (cvtinf.nfmts == 1) {
          while (morerec) {
               dfcDspRecs(++currec,totrec);
               procrec(cvtinf.srcfmts,cvtinf.dstfmts,dstbb,dfaLastLen());
               morerec=getnxtrec(srcbb);
          }
     }
     else {
          while (morerec) {
               sfmtptr=dfmtptr=NULL;
               for (i=0 ; i < cvtinf.nfmts ; i++) {
                    if (strncmp(cvtinf.srcbuf+cvtinf.indoffset,
                                cvtinf.fmtibufs[i],cvtinf.indbuflen) == 0) {
                         sfmtptr=&cvtinf.srcfmts[i];
                         dfmtptr=&cvtinf.dstfmts[i];
                         break;
                    }
               }
               if (sfmtptr == NULL) {
                    catastro("PROCDATFIL: Unable to find format indicated in "
                             "source buffer!");
               }
               dfcDspRecs(++currec,totrec);
               procrec(sfmtptr,dfmtptr,dstbb,dfaLastLen());
               morerec=getnxtrec(srcbb);
          }
     }
     dfcClrRecs();
     dfaClose(srcbb);
     srcbb=NULL;
     dfaClose(dstbb);
     dstbb=NULL;
}

static GBOOL                       /*   TRUE=recs 2 cvt, & enough HD room  */
ckrecshdrm(VOID)                   /* check HD room, if recs to convert    */
{
     LONG dfree,spcneed;
     struct ffblk fb;

     if (!fndfile(&fb,cvtinf.srcdatfil,0)) {
          catastro("CKRECSHDRM: Counldn't find %s!",cvtinf.srcdatfil);
     }
     dfree=dskfre(NULL);
     if (cvtinf.cvttype == DFC_NORMAL) {
          dfaSetBlk(srcbb);
          if ((totrec=dfaCountRec()) == 0L) {
               return(FALSE);
          }
          spcneed=(LONG)((cvtinf.ratio*fb.ff_fsize)/1024);
     }
     else if (cvtinf.cvttype == DFC_GENDB) {
          dfaSetBlk(srcbb);
          if (dfaCountRec() == 0L || !get1strec(srcbb)) {
               return(FALSE);
          }
          totrec=NODISP;
          spcneed=(LONG)((cvtinf.ratio*fb.ff_fsize)/1024);
     }
     else {
          return(TRUE);
     }
     if (dfree < spcneed) {
          catastro("Insufficient disk space to convert data file %s!\n"
                    "(%ldK free, %ldK required)",
                    cvtinf.srcdatfil,dfree,spcneed);
     }
     return(TRUE);
}

static GBOOL                       /*   TRUE=got record ok                 */
get1strec(                         /* get first record from data file      */
DFAFILE *datbb)                    /*   data file pointer                  */
{
     ASSERT(datbb != NULL);
     ASSERT(cvtinf.recorder == PHYSICAL
         || (cvtinf.recorder == KEY && cvtinf.keyno >= 0));
     dfaSetBlk(datbb);
     switch (cvtinf.recorder) {
     case PHYSICAL:
          return(dfaStepLO(cvtinf.srcbuf));
     case KEY:
          switch (cvtinf.cvttype) {
          case DFC_NORMAL:
               return(dfaAcqLO(cvtinf.srcbuf,cvtinf.keyno));
          case DFC_GENDB:
               ASSERT(cvtinf.keyno == 0 || cvtinf.keyno == 1);
               if (cvtinf.keyno == 0) {
                    return(dfaAcqEQ(cvtinf.srcbuf,&cvtinf.gendbkey,0));
               }
               else {
                    return(dfaAcqEQ(cvtinf.srcbuf,cvtinf.gendbkey.module,1));
               }
          }
     }
     return(FALSE);  // avoid compiler warning
}

static GBOOL                       /*   TRUE=got record ok                 */
getnxtrec(                         /* get next record from data file       */
DFAFILE *datbb)                    /*   data file pointer                  */
{
     GBOOL match;

     ASSERT(datbb != NULL);
     ASSERT(cvtinf.recorder == PHYSICAL
         || (cvtinf.recorder == KEY && cvtinf.keyno >= 0));
     dfaSetBlk(datbb);
     switch (cvtinf.recorder) {
     case PHYSICAL:
          return(dfaStepNX(cvtinf.srcbuf));
     case KEY:
          if (dfaQueryNX()) {
               switch (cvtinf.cvttype) {
               case DFC_NORMAL:
                    dfaAcqAbs(cvtinf.srcbuf,dfaAbs(),cvtinf.keyno);
                    return(TRUE);
               case DFC_GENDB:
                    setmem(gendbbuf,GENSIZ,0);
                    dfaAcqAbs(gendbbuf,dfaAbs(),cvtinf.keyno);
                    ASSERT(cvtinf.keyno == 0 || cvtinf.keyno == 1);
                    if (cvtinf.keyno == 0) {
                         match=sameas(gendbbuf,cvtinf.gendbkey.userid)
                            && sameas(gendbbuf+UIDSIZ,cvtinf.gendbkey.module);
                    }
                    else {
                         match=sameas(gendbbuf+UIDSIZ,cvtinf.gendbkey.module);
                    }
                    if (match) {
                         movmem(gendbbuf,cvtinf.srcbuf,cvtinf.srcbuflen);
                         return(TRUE);
                    }
               }
          }
     }
     return(FALSE);
}

static VOID
procrec(                           /* process single record from data file */
struct fmtinfo *sfmtptr,           /*   source format                      */
struct fmtinfo *dfmtptr,           /*   destination format                 */
DFAFILE *dstbb,                    /*   dest data file pointer             */
USHORT lstreclen)                  /*   length of last source record read  */
{
     USHORT lstoffset,vardatlen,reclen,i;
     struct fldinfo *sfldptr,*dfldptr;

     for (i=0 ; i < dfmtptr->nflds ; i++) {
          dfldptr=&dfmtptr->fields[i];
          if ((sfldptr=fndfld(sfmtptr,dfldptr->fldtag)) == NULL) {
               movmem(dfldptr->defval,cvtinf.cvtbuf+dfldptr->cvtoffset,
                      dfldptr->fldlen);
          }
          else {
               mov2cvt(sfldptr,dfldptr);
          }
     }
     if (!cvtinf.variable) {
          cvtData(cvtinf.cvtbuf,cvtinf.dstbuf,cvtinf.cvtbuflen,dfmtptr->fmtFDA,
                  cvtinf.srccvtflgs,cvtinf.dstcvtflgs,CHAN_NUL);
     }
     else {
          lstoffset=sfmtptr->fields[sfmtptr->nflds-1].srcoffset;
          vardatlen=lstreclen-lstoffset;
          if (dfldptr->varlen && dfldptr->maxcopy > 0) {
               if (dfldptr->fldtyp == T_CHAR || dfldptr->fldtyp == T_OPAQUE) {
                    vardatlen-=(vardatlen-dfldptr->maxcopy);
               }
               else {
                    vardatlen-=(vardatlen-dfldptr->maxcopy*dfldptr->fldlen);
               }
          }
          lstoffset=dfmtptr->fields[dfmtptr->nflds-1].cvtoffset;
          cvtData(cvtinf.cvtbuf,cvtinf.dstbuf,lstoffset+vardatlen,
                  dfmtptr->fmtFDA,cvtinf.srccvtflgs,cvtinf.dstcvtflgs,CHAN_NUL);
     }
     dfaSetBlk(dstbb);
     if (!cvtinf.variable) {
          dfaInsert(cvtinf.dstbuf);
     }
     else {
          lstoffset=dfmtptr->fields[dfmtptr->nflds-1].dstoffset;
          reclen=lstoffset+vardatlen;
          dfaInsertV(cvtinf.dstbuf,reclen);
     }
     clrcvtbufs();
}

static VOID
mov2cvt(                           /* move source fld data to cvtinf.cvtbuf*/
struct fldinfo *sfldptr,           /*   source field                       */
struct fldinfo *dfldptr)           /*   cooresponding destination field    */
{
     CHAR *srcbufptr,*cvtbufptr,*ocvtbufptr;
     USHORT len;

     srcbufptr=cvtinf.srcbuf+sfldptr->srcoffset;
     cvtbufptr=cvtinf.cvtbuf+dfldptr->cvtoffset;

     if (dfldptr->fldtyp == T_STRUCT) {
          len=dfldptr->fldlen;
          if (!cvtinf.srcpacked) {
               adjoff(&len,dfldptr->largest);
          }
          movmem(srcbufptr,cvtbufptr,len);
     }
     else if ((dfldptr->fldtyp != T_CHAR && dfldptr->fldtyp != T_OPAQUE)
           || dfldptr->fldlen == sfldptr->fldlen) {
          if (dfldptr->fldtyp == T_CHAR) {
               if (dfldptr->varlen && dfldptr->maxcopy > 0) {
                    len=dfldptr->maxcopy;
               }
               else {
                    len=dfldptr->fldlen-1;
               }
          }
          else if (dfldptr->fldtyp == T_OPAQUE) {
               if (dfldptr->varlen && dfldptr->maxcopy > 0) {
                    len=dfldptr->maxcopy;
               }
               else {
                    len=dfldptr->fldlen;
               }
          }
          else {
               if (dfldptr->varlen && dfldptr->maxcopy > 0) {
                    len=dfldptr->fldlen*dfldptr->maxcopy;
               }
               else {
                    len=dfldptr->fldlen*dfldptr->nelems;
               }
          }
          movmem(srcbufptr,cvtbufptr,len);
     }
     else if (dfldptr->fldlen < sfldptr->fldlen) {
          if (dfldptr->varlen && dfldptr->maxcopy > 0) {
               len=dfldptr->maxcopy;
          }
          else {
               len=dfldptr->fldlen;
               if (dfldptr->fldtyp == T_CHAR) {
                    len--; // account for terminating \0 for strings
               }
          }
          if (dfldptr->truncate == RIGHT) {
               movmem(srcbufptr,cvtbufptr,len);
          }
          else {
               srcbufptr+=(sfldptr->fldlen-dfldptr->fldlen);
               movmem(srcbufptr,cvtbufptr,len);
          }
     }
     else { // dfldptr->fldlen > sfldptr->fldlen
          if (dfldptr->justify == LEFT) {
               if (dfldptr->varlen && dfldptr->maxcopy > 0) {
                    len=dfldptr->maxcopy;
               }
               else {
                    len=sfldptr->fldlen;
                    if (dfldptr->fldtyp == T_CHAR) {
                         len--; // account for terminating \0 for strings
                    }
               }
               movmem(srcbufptr,cvtbufptr,len);
               setmem(cvtbufptr+len,
                      dfldptr->fldlen-sfldptr->fldlen,dfldptr->pad);
          }
          else {
               ocvtbufptr=cvtbufptr;
               cvtbufptr+=(dfldptr->fldlen-sfldptr->fldlen);
               if (dfldptr->varlen && dfldptr->maxcopy > 0) {
                    len=dfldptr->maxcopy;
               }
               else {
                    len=sfldptr->fldlen;
                    if (dfldptr->fldtyp == T_CHAR) {
                         len--; // account for terminating \0 for strings
                    }
               }
               movmem(srcbufptr,cvtbufptr,len);
               setmem(ocvtbufptr,len,dfldptr->pad);
          }
     }
}


static struct fldinfo *            /*   returns pointer to field (or NULL) */
fndfld(                            /* find field in format                 */
struct fmtinfo *fmtptr,            /*   format to search                   */
CHAR *fldtag)                      /*   field to look for                  */
{
     USHORT i;

     for (i=0 ; i < fmtptr->nflds ; i++) {
          if (sameas(fmtptr->fields[i].fldtag,fldtag)) {
               return(&fmtptr->fields[i]);
          }
     }
     return(NULL);
}

static VOID
clrcvtbufs(VOID)                   /* clear buffers in cvtinf              */
{
     setmem(cvtinf.srcbuf,cvtinf.srcbuflen,0);
     setmem(cvtinf.cvtbuf,cvtinf.cvtbuflen,0);
     setmem(cvtinf.dstbuf,cvtinf.dstbuflen,0);
}
