/****************************************************************************
 *                                                                          *
 *   WGSDFCVT.C                                                             *
 *                                                                          *
 *   Copyright (c) 1996-1997 GALACTICOMM, Inc.   All Rights Reserved.       *
 *                                                                          *
 *   Data file conversion utility.                                          *
 *                                                                          *
 *                                           - Bill Hyatt        6/25/96    *
 *                                           - Wally Muharsky    11/19/97   *
 ****************************************************************************/
#include "gcomm.h"
#ifdef GCWINNT
#include <windows.h>
#endif // GCWINNT
#include "majorbbs.h"
#include "gcspsrv.h"
#include "process.h"
#include "protstuf.h"
#include "excphand.h"
#include "dfcapi.h"
#include "dfcapi2.h"
#include "dfcverif.h"

#define FILREV "$Revision: 32 $"

#define ODDIRSIZ    60             /* max len of old data directory        */
#define KEEPDISCSIZ 7              /* size of "KEEP"/"DISCARD" chars       */
#define CVTLCHCSIZ  (CVTDESCSIZ+3+KEEPDISCSIZ)

/* current active item on screen */
#define CONVLIST 0                 /* conversion choose list               */
#define EDTODDIR 1                 /* editing old data directory           */

extern
CHAR scntbl[][GVIDSCNSIZ];         /* pre-obj'd screen files               */

CHAR savscn[GVIDSCNSIZ],           /* save current screen                  */
     olddatdir[ODDIRSIZ+1],        /* dir for old data files if kept       */
     **cvtdsclst=NULL;             /* convert descriptions for choose list */

INT savcur,                        /* save cursor size                     */
    ncvt=0;                        /* # of conversion to do                */

struct cvtlent {                   /* information on a conversion          */
     CHAR cvtfil[GCMAXPTH];        /*   description file                   */
     CHAR srcdatfil[GCMAXPTH];     /*   source data file                   */
     CHAR dstdatfil[GCMAXPTH];     /*   destination data file              */
     GBOOL keepsrc;                /*   keep source data file?             */
     USHORT cvttype;               /*   type of conversion                 */
} *cvtlst=NULL;                    /* list of conversion info              */

GBOOL vidinit=FALSE;

static DFCFUNCTION
ConversionFunction(                /*  Inline conversion function          */
pDFCFILEINFO from,                 /*  Pointer to from data info           */
pDFCFILEINFO to);                  /*  Pointer to to data info             */

static VOID oldinit(VOID);
static VOID docvt(VOID);
static VOID rencvt(CHAR *cvtfil);
static VOID xit(INT xitcod);

static VOID
RunCustomConvert(                  /*  Run custom conversion program       */
const CHAR *File);                 /*  File to run                         */

static VOID
SetConversionFunction(VOID);       /*  Set up conversion functions         */

static GBOOL                       /*  TRUE if it is a new file            */
IsNewFile(                         /* Is this a new version of CVT         */
const CHAR *File);                 /*  File to check                       */

static VOID
newinit(VOID);                     /*  New initialization routines         */

static CHAR *                      /*  Pointer to new file                 */
cvtdir(                            /* Conversion directory creator         */
const CHAR *File);                 /*  File to create full path on         */

static DFCPFCONVERT PlatformFunctions[]={
     {DFC_DOS, DFC_WNT,  ConversionFunction},
     {0,0,NULL},
};

static VOID
InitVideo(VOID);                   /* Initialize Video                     */

#ifdef GCWINNT
INT WINAPI
WinMain(
HINSTANCE hInstance,               // handle of current instance
HINSTANCE hPrevInstance,           // handle of previous instance
LPSTR pCmdLine,                    // pointer to command line
INT nCmdShow)                      // show state of window
{
#else
INT
main(
INT argc,                          // number of command line arguments
CHAR *argv[])                      // array of command line arguments
{
#endif // GCWINNT
     INT errcod;
TRY

#ifdef GCWINNT
     (VOID)hInstance;
     (VOID)hPrevInstance;
     (VOID)nCmdShow;
     if (!canRunUtil()) {
          MessageBox(NULL,NOPROCEED,"Unable To Run Data File Conversion",
                     MB_ICONSTOP|MB_OK|MB_TASKMODAL|MB_SETFOREGROUND);
          return(1);
     }
#endif // GCWINNT

     protinit("WGSDFCVT ");
     if ((errcod=setjmp(disaster)) != 0) {
          if (srcbb != NULL) {
               dfaClose(srcbb);
          }
          if (dstbb != NULL) {
               dfaClose(dstbb);
          }
          exit(errcod);
     }

#ifdef GCWINNT
     if (sameas(skptwht((CHAR *)pCmdLine),"debug")) {
          dbgcvt=TRUE;
     }
#else
     if (argc > 1 && sameas(argv[1],"debug")) {
          dbgcvt=TRUE;
     }
#endif // GCWINNT
     newinit();

#ifdef GCWINNT
     getUpdType();  //  Put here to force registry update and file check
#endif
     oldinit();
     InitVideo();
     if (ncvt != 0) {
          docvt();
     }

     dfcDeleteBackupFile();

     xit(0);
EXCEPT
     return(1);
}

static VOID
oldinit(VOID)                      /* get convert info, set up screen      */
{
     struct ffblk cvtfb;
     GBOOL morecvt;
     static CHAR cvtfildir[GCMAXPTH],cvtfilspec[GCMAXPTH],cvtfil[GCMAXPTH];

     normspec(cvtfildir,CVTFILDIR);
     if (cvtfildir[0] == '\0') {
          InitVideo();
          catastro("INIT: Unable to obtain conversion template file directory "
                   "from %s!",CVTFILDIR);
     }
     sprintf(cvtfilspec,"%s"SLS"*.CVT",cvtfildir);
     morecvt=fnd1st(&cvtfb,cvtfilspec,0);
     while (morecvt) {
          sprintf(cvtfil,"%s"SLS"%s",cvtfildir,cvtfb.ff_name);
          if (shouldcvt(cvtfil)) {
               cvtdsclst=(CHAR **)alcrsz(cvtdsclst,sizeof(CHAR *)*ncvt,
                                                   sizeof(CHAR *)*(ncvt+1));
               cvtdsclst[ncvt]=alczer(CVTLCHCSIZ+1);
               sprintf(cvtdsclst[ncvt],"%s",cvtinf.cvtdsc);
               cvtlst=(struct cvtlent *)alcrsz(cvtlst,
                                               sizeof(struct cvtlent)*ncvt,
                                               sizeof(struct cvtlent)*(ncvt+1));
               stlcpy(cvtlst[ncvt].cvtfil,cvtfil,GCMAXPTH);
               stlcpy(cvtlst[ncvt].srcdatfil,cvtinf.srcdatfil,GCMAXPTH);
               stlcpy(cvtlst[ncvt].dstdatfil,cvtinf.dstdatfil,GCMAXPTH);
               cvtlst[ncvt].keepsrc=TRUE;
               cvtlst[ncvt].cvttype=cvtinf.cvttype;
               ncvt++;
          }
          //  I don't quite understand what this was here for?  I could
          //  not think of a good reason, so I removed it.  WAM

          //else if (!fnd1st(&fb,cvtinf.srcdatfil,0)) {
          else {
               rencvt(cvtfil);
          }
          morecvt=fndnxt(&cvtfb);
     }
}

static VOID
docvt(VOID)                        /* do conversions                       */
{
     INT i;
     GBOOL dirchkd=FALSE;
     CHAR cwdir[GCMAXPTH],savspc[GCMAXPTH],srcnam[GCMAXPTH],
          srcdrv[MAXDRIVE],savdrv[MAXDRIVE],*srcptr;
     FILE *cfgfp;

     if (ncvt > 0) {
#ifdef GCWINNT
          WriteOldRegistry();
#endif
          dfcAskBackup();
          stlcpy(olddatdir,dfcBackupDirectory(),GCMAXPTH);
     }
     for (i=0 ; i < ncvt ; i++) {
          dfcDspCvtNum(i+1,ncvt);
          dfcDspCvtDesc(cvtdsclst[i]);
          dfcDspFiles(cvtlst[i].srcdatfil,cvtlst[i].dstdatfil);
          switch (cvtlst[i].cvttype) {
          case DFC_NORMAL:
               cvtdatfil(cvtlst[i].cvtfil,cvtlst[i].srcdatfil,
                         cvtlst[i].dstdatfil,cvtlst[i].keepsrc);
               if (cvtlst[i].keepsrc && dfcBackupRequired()) {
                    if (!dirchkd) {
                         getcwd(cwdir,GCMAXPTH);
                         if (chdir(olddatdir) == -1) {
                              if (MKDIR(olddatdir) == -1) {
                                   catastro("DOCVT: Unable to create "
                                            "directory %s for old data files!",
                                            olddatdir);
                              }
                         }
                         chdir(cwdir);
                         dirchkd=TRUE;
                    }
                    fileparts(GCPART_FNAM,cvtlst[i].srcdatfil,srcnam,
                              sizeof(srcnam));
                    sprintf(savspc,"%s"SLS"%s",olddatdir,srcnam);
                    fileparts(GCPART_DRVS,cvtlst[i].srcdatfil,srcdrv,
                              sizeof(srcdrv));
                    fileparts(GCPART_DRVS,savspc,savdrv,sizeof(savdrv));
                    srcptr=cvtinf.tmpsrcnam == NULL ? cvtlst[i].srcdatfil
                                                    : cvtinf.tmpsrcnam;
                    if (sameas(srcdrv,savdrv)) {
                         rename(srcptr,savspc);
                    }
                    else {
                         movefile(srcptr,savspc);
                    }
               }
               else {
                    if (cvtinf.tmpsrcnam == NULL) {
                         unlink(cvtlst[i].srcdatfil);
                    }
                    else {
                         unlink(cvtinf.tmpsrcnam);
                    }
               }
               break;
          case DFC_GENDB:
               cvtdatfil(cvtlst[i].cvtfil,GDBSOURCE,NULL,TRUE);
          }
          rencvt(cvtlst[i].cvtfil);
     }

     if ((cfgfp=fopen(CVTCFGFIL,FOPWA)) == NULL) {
          catastro("DOCVT: Unable to open %s for write!",CVTCFGFIL);
     }
     fprintf(cfgfp,"%s\n",olddatdir);
     fclose(cfgfp);
     xit(0);
}

static VOID
rencvt(                            /* rename .CVT file to not repeat cvt   */
CHAR *cvtfil)                      /*   .CVT file to rename                */
{
     CHAR renspc[GCMAXPTH],cvtnam[GCMAXFNM];

     fileparts(GCPART_PATH,cvtfil,renspc,sizeof(renspc));
     fileparts(GCPART_FILE,cvtfil,cvtnam,sizeof(cvtnam));
     strcat(renspc,spr("%s.%s",cvtnam,CVTDONEXT));
     unlink(renspc);
     rename(cvtfil,renspc);
}

static VOID
xit(                               /* exit routine                         */
INT xitcod)                        /*   code to exit with                  */
{
     INT i;

     clrcvtinf();
     if (ncvt > 0) {
          for (i=0 ; i < ncvt ; i++) {
               free(cvtdsclst[i]);
          }
          free(cvtdsclst);
          free(cvtlst);
     }
     if (vidinit) {
          mem2scn(savscn,0,GVIDSCNSIZ);
          cursiz(savcur);
          clsvid();
          clrscr();
#ifdef GCWINNT
          FreeConsole();
#endif // GCWINNT
     }
     exit(xitcod);
}

VOID
appgprept(VOID)                      /* application-specific GP info (stub) */
{
}

VOID
appgprecd(VOID)                    /* application-specific GP record (stub) */
{
}

static VOID
newinit(VOID)                      /*  New initialization routines         */
{
     struct ffblk fb;
     struct ffblk fb2;
     CHAR newname[GCMAXPTH];
     INT ans;
     CHAR *ptr;

     InitVideo();
     dfcDeleteBackupFile();
     if (fnd1st(&fb,cvtdir("*.CVT"),0)) {
          do {
               if (IsNewFile(cvtdir(fb.ff_name))) {
                    ans=dfcVerifyFile(cvtdir(fb.ff_name));
                    if (ans != DFC_SUCCESS) {
                         if (cf.Developer != NULL) {
                              dfcError("Error in conversion template %s (Line %d)\nDeveloper=%s,\nError=%s\n\n",
                               fb.ff_name,LinesRead,cf.Developer,dfcErrorStg(ans),dfcbuf);
                              return;
                         }
                         else {
                              dfcError("Error in conversion template %s.",fb.ff_name);
                         }
                    }
                    if (cf.CustomProgram != NULL) {
                         RunCustomConvert(cf.CustomProgram);
                    }
                    else {
                         SetConversionFunction();
                         if (cf.Generic) {
                              dfcRegisterGDB(cf.Description,cf.fillst,
                               PlatformFunctions,cf.ModuleName);
                         }
                         else {
                              dfcRegister(cf.Description,cf.fillst,
                               PlatformFunctions,-1,DFC_FORWARD);
                         }
                    }
                    dfcVerifyFree();
               }
               else {//Old Style is still handled by old init routine
                    continue;
               }
               stlcpy(newname,cvtdir(fb.ff_name),GCMAXPTH);
               if ((ptr=strchr(newname,'.')) != NULL) {
                    *ptr='\0';
               }
               stlcat(newname,".CV_",GCMAXPTH);
               if (fndfile(&fb2,newname,0) && unlink(newname) == -1) {
                    if (chmod(newname,S_IREAD|S_IWRITE) == -1
                     || unlink(newname) == -1) {
                         dfcError("Unable to remove file %s.",newname);
                    }
               }
               if (rename(cvtdir(fb.ff_name),newname)== -1) {
                    dfcError("Unable to rename %s to %s.",cvtdir(fb.ff_name),
                     newname);
               }
          } while (fndnxt(&fb));
     }
}

static CHAR *                      /*  Pointer to new file                 */
cvtdir(                            /* Conversion directory creator         */
const CHAR *File)                  /*  File to create full path on         */
{
     static CHAR FullFile[GCMAXPTH];

     setmem(FullFile,GCMAXPTH,0);
     stlcpy(FullFile,CVTFILDIR,GCMAXPTH);
     stlcat(FullFile,SLS,GCMAXPTH);
     stlcat(FullFile,File,GCMAXPTH);
     return(FullFile);
}

static GBOOL                       /*  TRUE if it is a new file            */
IsNewFile(                         /* Is this a new version of CVT         */
const CHAR *File)                  /*  File to check                       */
{
     if (tfsopn(File) > 0) {
          while (tfsrdl() != TFSDUN) {
               switch (tfstate) {
               case TFSLIN:
                    if (tfspfx("Developer")) {
                         tfsabt();
                         return(TRUE);
                    }
               }
          }
     }
     return(FALSE);
}

static DFCFUNCTION
ConversionFunction(                /*  Inline conversion function          */
pDFCFILEINFO from,                 /*  Pointer to from data info           */
pDFCFILEINFO to)                   /*  Pointer to to data info             */
{
     pDFCOFFSETS foff;
     pDFCOFFSETS toff;
     struct flddef *fda;
     UINT elems;
     UINT telems;
     UINT felems;
     pDFCFIELDINFO finf;
     GBOOL VariableStruct=FALSE;
     UINT VariableSize=0;

     setmem(to->Data,to->DataLength,0);
     for (toff=to->Offsets; toff->Type != DFCFLD_END; toff++) {
          if (toff->Type == DFCFLD_STRUCT) {
               continue;
          }
          if (toff->Type == DFCFLD_VSTRUCT) {
               VariableStruct=TRUE;
               continue;
          }
          for (foff=from->Offsets; foff->Type != DFCFLD_END; foff++) {
               if (sameas(toff->Name,foff->Name)) {
                    if (VariableStruct) {
                         if (foff->Offset >= from->DataLength) {
                              if (VariableSize == 0) {
                                   VariableSize=toff->Offset;
                              }
                              break;
                         }
                    }

                    finf=dfcGetFieldInfo(to->Platform,to->Name,toff->Name);
                    telems=toff->nelems;
                    felems=foff->nelems;
                    if (finf->Flags&DFCFIELD_VARIABLE) {
                         felems=(from->DataLength-foff->Offset)/
                          dfcFieldSize(foff->Type);
                    }
                    elems=min(telems,felems);
                    fda=dfcGetFDA(toff->Type);
                    if (finf->Flags&DFCFIELD_VARIABLE) {
                         to->DataLength=(to->DataLength-
                          dfcFieldSize(toff->Type)*telems)+elems;
                         telems=elems;
                    }

                    setmem(&to->Data[toff->Offset],dfcFieldSize(toff->Type)*
                     elems,finf->Pad);

                    if (finf->Flags&DFCFIELD_JUSTIFY_RIGHT) {
                         toff->Offset+=((dfcFieldSize(toff->Type)*telems)-
                          (dfcFieldSize(foff->Type)*felems));
                    }
                    if (finf->Flags&DFCFIELD_TRUNCATE_LEFT) {
                         foff->Offset+=((dfcFieldSize(foff->Type)*felems)-
                          (dfcFieldSize(toff->Type)*telems));
                    }
                    cvtData(&from->Data[foff->Offset],
                     &to->Data[toff->Offset],
                     dfcFieldSize(toff->Type)*telems,
                     fda,
                     from->Type,
                     to->Type,
                     CHAN_NUL);
                    setmem(foff->Name,DFC_NAMSIZ,0);
                    break;
               }
          }
     }
     if (VariableStruct) {
          to->DataLength=VariableSize;
     }
     return(to->Data);
}

static VOID
SetConversionFunction(VOID)        /*  Set up conversion functions         */
{
     pDFCLIST lptr;
     pDFCFILE fptr;

     lptr=cf.fillst;
     while (lptr->Platform != DFC_END) {
          fptr=lptr->file;
          while (strlen(fptr->Name) > 0) {
               fptr->cnvfunc=ConversionFunction;
               fptr++;
          }
          lptr++;
     }
}

static VOID
RunCustomConvert(                  /*  Run custom conversion program       */
const CHAR *File)                  /*  File to run                         */
{
#ifdef GCDOS
     INT i;

     if ((i=spawnv(P_WAIT,(CHAR*)File,NULL)) != 0) {
          catastro("SPAWNCVT: Error launching %s (error %d)!",File,i);
     }
#endif
#ifdef GCWINNT
     STARTUPINFO si;
     PROCESS_INFORMATION pi;
     DWORD procrc;

     memset(&si,0,sizeof(STARTUPINFO));
     si.cb=sizeof(STARTUPINFO);
     //si.dwFlags=STARTF_USESTDHANDLES;
     if (CreateProcess(File,NULL,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)!",
                        File,(INT)procrc);
          }
     }
     else {
          catastro("SPAWNCVT: CreateProcess error launching %s (error %d)!",
                   File,(INT)GetLastError());
     }
#endif
}

static VOID
InitVideo(VOID)                    /* Initialize Video                     */
{
     if (!vidinit) {
#ifdef GCWINNT
          AllocConsole();
#endif // GCWINNT
          initvid();
          vidinit=TRUE;
          savcur=curcurs();
     }
}