
/***************************************************************************
 *                                                                         *
 *   INTEGROU.CPP                                                          *
 *                                                                         *
 *   Copyright (c) 1992-1996 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   These routines perform the dynamic integration functions built into   *
 *   WGSINT and WGSMTREE.                                                  *
 *                                                                         *
 *                                            - T. Stryker 2/12/92         *
 *                                            - R. Stein 2/16/93           *
 *                                                                         *
 ***************************************************************************/

#include "stdafx.h"
#include "sutl.h"
#include "cflict.h"
#include "resource.h"
#include "sutldlg.h"
#include "ReqDlg.h"
#include "catexcp.h"

INT nmdfs;                         // number of modules found in .MDF files
INT nmods;                         // number of modules referenced altogether
CHAR **modnmv,**modnmvcs;          // lists of module names from .MDF files
INT btrtfl;                        // Btrieve total number of file handles
GBOOL *anyUtils;                   // list of .MDFS that have offline utils

struct moddat *moddat;             // dynam alloc'd array of module data
struct refstf refstf;              // WGSMDF.REF header layout

#define DFTDSC "English version of BBS-ANSI (or ASCII)"       // dft descr
#define DFTEXTANS ".ans"           // default .ANS language file extension
#define DFTEXTASC ".asc"           // default .ASC language file extension
#define DFTEXTIBM ".ibm"           // default .IBM language file extension

static
struct lingo dftlang={DFTLNG,DFTDSC,DFTEXTANS,DFTEXTASC,DFTEXTIBM,
                      DFTEDR,DFTYES,DFTNO};

static INT nmiss;                  // number of missing required modules

FILE *bbsup=NULL,*bbsdn=NULL;      // WGSUP.BAT and WGSDN.BAT files
FILE *cnfup=NULL,*cnfdn=NULL;      // CNFUP.BAT and CNFDN.BAT files
CHAR *chantyps;                    // extra channel types
CHAR *hdwrtyps;                    // extra hardware types

static GBOOL csfopend=FALSE;       // client/server files are opened

CHAR *mmappid;              // main menu appid of the day

VOID
iniint(VOID)                       // initialize module integrator biz
{
     bldmda();
     while (!dupsok()) {
          free(moddat);
          moddat=NULL;
          free(anyUtils);
          anyUtils=NULL;
          bldmda();
     }
     if (!refsok()) {
          ctrefs();
     }
     nmiss=0;
     warnem();
}

CHAR **
avlmods(                           // return an array of ptrs to module names
GBOOL csedit)                      // ...if TRUE, C/S
{
     INT i,j;

     if (csedit) {
          if (modnmvcs != NULL) {
               free(modnmvcs);
          }
          modnmvcs=(CHAR **)alczer((nmdfs+1)*sizeof(CHAR *));
     }
     else {
          if (modnmv != NULL) {
               free(modnmv);
          }
          modnmv=(CHAR **)alczer((nmdfs+1)*sizeof(CHAR *));
     }
     for (i=0,j=0 ; i < nmdfs ; i++) {
          if (moddat[i].modnam[0] != '\0'
             && !(moddat[i].flags&INTERN)) {
               if (!csedit && !(moddat[i].flags&CSONLY)) {
                    modnmv[j++]=moddat[i].modnam;
               }
               if (csedit && (moddat[i].flags&HASCLX)) {
                    modnmvcs[j++]=moddat[i].modnam;
               }
          }
     }
     sortstgs(csedit ? modnmvcs : modnmv,j);
     return(csedit ? modnmvcs : modnmv);
}

VOID
clrefs(VOID)                       // clear all module reference counts
{
     INT i;

     for (i=0 ; i < nmods ; i++) {
          moddat[i].nrefs=0;
     }
}

VOID
incref(                            // increment count of references to modnam
CHAR *modnam)
{
     INT i;

     if (!sameas(modnam,MMMODN)) {
          for (i=0 ; i < nmods ; i++) {
               if (sameas(modnam,moddat[i].modnam)) {
                    moddat[i].nrefs++;
                    return;
               }
          }
          addxmod();
          anyUtils[nmods-1]=FALSE;
          memset(&moddat[nmods-1],0,sizeof(struct moddat));
          stzcpy(moddat[nmods-1].modnam,modnam,MNMSIZ);
          moddat[nmods-1].nrefs=1;
     }
}

VOID
decref(                            // decrement count of references to modnam
CHAR *modnam)
{
     INT i;

     CCatException* drExcept=new CCatException;
     if (!sameas(modnam,MMMODN)) {
          for (i=0 ; i < nmods ; i++) {
               if (sameas(modnam,moddat[i].modnam)) {
                    if (moddat[i].nrefs-- == 0) {
                         drExcept->SetError(ERROR_FAIL,"Fatal error in decref() - \"%s\" < 0",
                              modnam);
                         THROW(drExcept);
                    }
                    drExcept->Delete();
                    return;
               }
          }
          drExcept->SetError(ERROR_FAIL,"Fatal error in decref() - Missing \"%s\"",modnam);
          THROW(drExcept);
     }
     drExcept->Delete();
}

VOID
finint(VOID)                       // write WGSERV.CFG, WGSMDF.REF, WGSINT.REF, BBSMDFS.LST
{                                  // and WGSBTR.BAT (call only once per iniint())
     reqchk();
     wrtact();
     wrtmjr();
     wrtmdf();
     wrtutl();
     opncsf();
     ssfapp();
     rfrapp();
     ssfiler();
     clscsf();
     if (modnmv != NULL) {
          free(modnmv);
          modnmv=NULL;
     }
     if (modnmvcs != NULL) {
          free(modnmvcs);
          modnmvcs=NULL;
     }
     if (moddat != NULL) {
          free(moddat);
          moddat=NULL;
     }
     if (anyUtils != NULL) {
          free(anyUtils);
          anyUtils=NULL;
     }
     record();
}

VOID
reqchk(VOID)                       // tag all "required" mods; warn if any gone
{                                  // leaves MISSREQ file behind iff so
     INT i;
     FILE *fp;

     for (i=0 ; i < nmdfs ; i++) {
          if (moddat[i].nrefs != 0 || (moddat[i].flags&UNCOND)) {
               tagreq(&moddat[i]);
          }
     }
     if (nmiss > 0) {
          if ((fp=fopen(MISSREQ,FOPWA)) != NULL) {
               fprintf(fp,"One or more .MDF files appear to be missing "
                          "on your system...\n");
               fclose(fp);
          }
     }
     else {
          unlink(MISSREQ);
     }
}

VOID
wrtact(VOID)                       // write BBSMDFS.LST
{
     INT i,nacts;
     FILE *fp;

     CCatException* waExcept=new CCatException;
     if ((fp=fopen("wgsmdfs.lst",FOPWA)) == NULL) {
          waExcept->SetError(ERROR_FAIL,"Fatal error in wrtact() - Cannot open \"wgsmdfs.lst\"");
          THROW(waExcept);
     }
     nacts=0;
     for (i=0 ; i < nmdfs ; i++) {
          if (moddat[i].modnam[0] != '\0') {
               nacts++;
          }
     }
     fprintf(fp,"%d\n",nacts);
     for (i=0 ; i < nmdfs ; i++) {
          if (moddat[i].modnam[0] != '\0') {
               fprintf(fp,"%s.mdf\n",moddat[i].mdfnam);
          }
     }
     fclose(fp);
     waExcept->Delete();
}

static VOID
tagreq(                     // tag a mod and all it requires as required
struct moddat *mddptr)
{
     INT i,j;

     if (!(mddptr->flags&REQUIR)) {
          mddptr->flags|=REQUIR;
          for (i=0 ; i < MAXMDF ; i++) {
               if (mddptr->mdfreq[i][0] == '\0') {
                    break;
               }
               for (j=0 ; j < nmdfs ; j++) {
                    if (sameas(mddptr->mdfreq[i],moddat[j].mdfnam)) {
                         tagreq(&moddat[j]);
                         break;
                    }
               }
               if (j == nmdfs) {
                    reqwarn(mddptr->mdfreq[i]);
                    nmiss++;
               }
          }
     }
}

VOID
wrtmjr(VOID)                       // creates WGSERV.CFG from structures
{                                  // also computes btrtfl
     FILE *rfp;
     struct lingo *lp;
     INT i,j,k;
     CHAR **dllist;
     CHAR *gp,*mp,*tmpnam;
     struct moddat *md;

     CCatException* wmExcept=new CCatException;
     dllist=(CHAR **)alczer((nmods*MAXDLL+1)*sizeof(CHAR *));
     btrtfl=0;
     for (i=0 ; i < nmdfs ; i++) {
          if (moddat[i].flags&REQUIR) {
               btrtfl+=moddat[i].btrfls;
               for (j=0 ; j < MAXDLL ; j++) {
                    adddll(dllist,moddat[i].dllnam[j]);
               }
          }
     }
     if ((rfp=fopen("wgserv.cfg",FOPWA)) == NULL) {
          wmExcept->SetError(ERROR_FAIL,"Fatal error in wrtmjr() - Cannot create \"wgserv.cfg\"");
          THROW(wmExcept);
     }
     fprintf(rfp,"MaxPage=4096\nTotFiles=%u\nNLingo=%u\n",btrtfl,nlingo);
     for (i=0 ; dllist[i] != NULL ; i++) {
          fprintf(rfp,"DLL=%s\n",dllist[i]);
     }
     for (i=0 ; i < nmdfs ; i++) {
          if (moddat[i].modnam[0] != '\0') {
               if ((moddat[i].flags&CSTART) && (moddat[i].flags&HASCLX)) {
                    fprintf(rfp,"CSTART=%s",moddat[i].mdfnam);
                    if (moddat[i].stfunc != NULL) {
                         fprintf(rfp," %s %s",
                                 moddat[i].dllnam[0],moddat[i].stfunc);
                    }
                    fprintf(rfp,"\n");
               }
          }
     }
     for (i=0 ; i < nmdfs ; i++) {
          if (moddat[i].modnam[0] != '\0') {
               fprintf(rfp,"APP=%s %s\n",moddat[i].mdfnam,moddat[i].modnam);
          }
     }
     for (i=0 ; i < nlingo ; i++) {
          lp=languages[i];
          fprintf(rfp,"LNG=%s\n"
                      "LNGDSC=%s\n"
                      "LNGEXT=%s %s %s\n"
                      "LNGEDT=%s\n"
                      "LNGYES=%s\n"
                      "LNGNO=%s\n",
                      lp->name,lp->desc,lp->extans,lp->extasc,lp->extibm,
                      lp->editor,lp->yes,lp->no);
     }
     for (i=0 ; i < nmdfs ; i++) {
          if (moddat[i].msgs != NULL) {
               if ((mp=strtok(gp=alcdup(moddat[i].msgs),DELIMS)) != NULL) {
                    do {
                         fprintf(rfp,"MSG=%s\n",mp);
                    } while ((mp=strtok(NULL,DELIMS)) != NULL);
               }
               free(gp);
          }
     }
     for (i=0 ; i < nmdfs ; i++) {
          if ((moddat[i].flags&HADIND) && !sameas(moddat[i].ineed[0],ALLMDF)) {
               fprintf(rfp,"NEEDED4 %s",moddat[i].modnam);
               for (k=0 ; k < MAXMDF ; k++) {
                    tmpnam=moddat[i].ineed[k];
                    if (*tmpnam == '\0') {
                         break;
                    }
                    if ((md=fndapp(tmpnam)) != NULL) {
                         fprintf(rfp,"\t%s",md->modnam);
                    }
               }
               for (j=0 ; j < nmdfs ; j++) {
                    if (j != i) {
                         for (k=0 ; k < MAXMDF ; k++) {
                              tmpnam=moddat[j].needme[k];
                              if (*tmpnam == '\0') {
                                   break;
                              }
                              if (sameas(tmpnam,moddat[i].mdfnam)
                               || sameas(tmpnam,ALLMDF)) {
                                   fprintf(rfp,"\t%s",moddat[j].modnam);
                              }
                         }
                    }
               }
               fprintf(rfp,"\n");
          }
     }
     fprintf(rfp,"%s",chantyps);
     fprintf(rfp,"%s",hdwrtyps);
     fclose(rfp);
     free(dllist);
     wmExcept->Delete();
}

static VOID
adddll(                            // add a dll name to the dll list
CHAR **dllist,
CHAR *dllnam)
{
     INT k;

     if (dllnam[0] == '\0') {
          return;
     }
     for (k=0 ; dllist[k] != NULL ; k++) {
          if (sameas(dllnam,dllist[k])) {
               return;
          }
     }
     dllist[k]=dllnam;
}

VOID
wrtmdf(VOID)                       // creates WGSMDF.REF from iniint()'s struct
{
     FILE *efp;
     INT i;
     static struct moddat dskdat;

     pMW->ShowWindow(SW_SHOWNORMAL);
     pST->SetWindowText("Generating WGSMDF.REF....");
     if (fnd1st(&refstf.fb,"wgsmenu2.dat",0)) {
          if ((efp=fopen("wgsmdf.ref",FOPWB)) != NULL) {
               refstf.nmods=nmods;
               fwrite(&refstf,sizeof(struct refstf),1,efp);
               for (i=0 ; i < nmods ; i++) {
                    memmove(&dskdat,&moddat[i],sizeof(struct moddat));
                    dskdat.lp=NULL;
                    dskdat.msgs=NULL;
                    fwrite(&dskdat,sizeof(struct moddat),1,efp);
               }
               fclose(efp);
          }
     }
}

static VOID
wrtutl(VOID)                       // write out WGSUTL.REF file
{
     INT i;
     FILE *fp;

     pMW->ShowWindow(SW_SHOWNORMAL);
     pST->SetWindowText("Generating WGSUTL.REF....");
     if ((fp=fopen("wgsutl.ref",FOPWA)) != NULL) {
          for (i=0 ; i < nmods ; i++) {
               if (anyUtils[i] == TRUE) {
                    fprintf(fp,"%s.mdf\n",moddat[i].mdfnam);
               }
          }
          fclose(fp);
     }
}

static VOID
bldmda(VOID)                       // build the moddat array from .MDF files
{
     pMW->ShowWindow(SW_SHOWNORMAL);
     pST->SetWindowText("Collecting Module Information....");
     unlink("wgsup.bat");
     unlink("wgsdn.bat");
     unlink("cnfup.bat");
     unlink("cnfdn.bat");
     chantyps=alcdup("");
     hdwrtyps=alcdup("");
     if ((nmods=nmdfs=tfsopn("*.mdf")) > 0) {
          anyUtils=(GBOOL *)alczer(nmdfs*sizeof(GBOOL));
          moddat=(struct moddat *)alczer(nmdfs*sizeof(struct moddat));
          nmods=0;
          while (tfsrdl() != TFSDUN) {
               switch (tfstate) {
               case TFSLIN:
                    interp();
                    break;
               case TFSEOF:
                    wrapit();
                    break;
               }
          }
          nmdfs=nmods;
     }
     else {
          moddat=NULL;
          anyUtils=NULL;
     }
     if (bbsup != NULL) {
          fprintf(bbsup,":fin\n");
          fclose(bbsup);
          bbsup=NULL;
     }
     if (bbsdn != NULL) {
          fprintf(bbsdn,":fin\n");
          fclose(bbsdn);
          bbsdn=NULL;
     }
     if (cnfup != NULL) {
          fprintf(cnfup,":fin\n");
          fclose(cnfup);
          cnfup=NULL;
     }
     if (cnfdn != NULL) {
          fprintf(cnfdn,":fin\n");
          fclose(cnfdn);
          cnfdn=NULL;
     }
     dwreps();
     plinfo();
}

static VOID
interp(VOID)                  // interpret a line of .MDF file input
{
     CHAR *mp;
     struct lingo *lp;
     INT i,n,m;

     CCatException* ipExcept=new CCatException;
     if (tfsbuf[0] != ';') {
          if (tfspfx("Module Name:")) {
               stzcpy(moddat[nmods].modnam,tfspst,MNMSIZ);
          }
          else if (tfspfx("Developer:")) {
               stzcpy(moddat[nmods].devnam,tfspst,DNMSIZ);
          }
          else if (tfspfx("Install:")) {
               stzcpy(moddat[nmods].instal,tfspst,FNSIZE);
          }
          else if (tfspfx("Online user manual:")) {
               stzcpy(moddat[nmods].usrtxt,tfspst,FNEXSZ);
          }
          else if (tfspfx("Add-On Utility:")) {
               anyUtils[nmods]=TRUE;
          }
          else if (tfspfx("DLLs:")
             && (mp=strtok(tfspst,DELIMS)) != NULL) {
               for (i=0 ; i < MAXDLL ; i++) {
                    stzcpy(moddat[nmods].dllnam[i],mp,FNSIZE);
                    if ((mp=strtok(NULL,DELIMS)) == NULL) {
                         break;
                    }
               }
          }
          else if (tfspfx("Requires:")
             && (mp=strtok(tfspst,DELIMS)) != NULL) {
               for (i=0 ; i < MAXMDF ; i++) {
                    stzcpy(moddat[nmods].mdfreq[i],mp,FNSIZE);
                    if ((mp=strtok(NULL,DELIMS)) == NULL) {
                         break;
                    }
               }
          }
          else if (tfspfx("Replaces:")
             && (mp=strtok(tfspst,DELIMS)) != NULL) {
               for (i=0 ; i < MAXMDF ; i++) {
                    stzcpy(moddat[nmods].mdfrep[i],mp,FNSIZE);
                    if ((mp=strtok(NULL,DELIMS)) == NULL) {
                         break;
                    }
               }
          }
          else if (sameto("Unconditional",tfsbuf)) {
               moddat[nmods].flags|=UNCOND;
          }
          else if (sameto("Internal",tfsbuf)) {
               moddat[nmods].flags|=INTERN;
          }
          else if (sameto("C/S Startup",tfsbuf)) {
               moddat[nmods].flags|=CSTART;
               moddat[nmods].flags|=UNCOND;
          }
          else if (tfspfx("Startup Function:")) {
               if (moddat[nmods].stfunc != NULL) {
                    free(moddat[nmods].stfunc);
               }
               moddat[nmods].stfunc=alcdup(tfspst);
          }
          else if (sameto("C/S Only",tfsbuf)) {
               moddat[nmods].flags|=CSONLY;
          }
          else if (tfspfx("Default Icon:")) {
               if ((mp=strchr(tfspst,'.')) != NULL) {
                    *mp='\0';
               }
               stzcpy(moddat[nmods].defico,tfspst,FNSIZE);
          }
          else if (tfspfx("MSGs:")) {
               if (moddat[nmods].msgs == NULL) {
                    moddat[nmods].msgs=alcdup(tfspst);
               }
               else {
                    n=strlen(moddat[nmods].msgs);
                    m=strlen(tfspst);
                    moddat[nmods].msgs=(CHAR *)alcrsz(moddat[nmods].msgs,n+1,n+1+m+1);
                    sprintf(moddat[nmods].msgs+n," %s",tfspst);
               }
          }
          else if (tfspfx("Client app EXE:")) {
               moddat[nmods].flags|=HASCLX;
          }
          else if (sameto("I need:",tfsbuf)) {
               moddat[nmods].flags|=HADIND;
               if (tfspfx("I need:")) {
                    if ((mp=strtok(tfspst,DELIMS)) != NULL) {
                         for (i=0 ; i < MAXMDF ; i++) {
                              stzcpy(moddat[nmods].ineed[i],mp,FNSIZE);
                              if ((mp=strtok(NULL,DELIMS)) == NULL) {
                                   break;
                              }
                         }
                    }
               }
          }
          else if (tfspfx("Needs me:")
             && (mp=strtok(tfspst,DELIMS)) != NULL) {
               for (i=0 ; i < MAXMDF ; i++) {
                    stzcpy(moddat[nmods].needme[i],mp,FNSIZE);
                    if ((mp=strtok(NULL,DELIMS)) == NULL) {
                         break;
                    }
               }
          }
          else if (tfspfx("Language")) {
               if ((lp=moddat[nmods].lp) == NULL) {
                    lp=moddat[nmods].lp=(struct lingo *)
                                        alczer(sizeof(struct lingo));
               }
               tfsdpr();
               if (tfspfx(":")) {
                    stzcpy(lp->name,tfspst,LNGSIZ);
               }
               else if (tfspfx("Description:")) {
                    stzcpy(lp->desc,tfspst,LNGDSC);
               }
               else if (tfspfx("File Extension:")) {
                    stfext(lp,tfspst);
               }
               else if (tfspfx("Editor:")) {
                    stzcpy(lp->editor,tfspst,LNGEDT);
               }
               else if (tfspfx("Yes/No:") && (mp=strchr(tfspst,'/')) != NULL) {
                    *mp++='\0';
                    stzcpy(lp->yes,tfspst,LNGYN);
                    stzcpy(lp->no,mp,LNGYN);
               }
          }
          else if (tfspfx("WGS up:")) {
               if (bbsup == NULL && (bbsup=fopen("wgsup.bat",FOPWA)) == NULL) {
                    ipExcept->SetError(ERROR_FAIL,"interp() - Cannot create a new wgsup.bat");
                    THROW(ipExcept);
               }
               fprintf(bbsup,"%s\n",tfspst);
          }
          else if (tfspfx("WGS down:")) {
               if ((bbsdn == NULL) && ((bbsdn=fopen("wgsdn.bat",FOPWA)) == NULL)) {
                    ipExcept->SetError(ERROR_FAIL,"interp() - Cannot create a new wgsdn.bat");
                    THROW(ipExcept);
               }
               fprintf(bbsdn,"%s\n",tfspst);
          }
          else if (tfspfx("CNF up:")) {
               if (cnfup == NULL && (cnfup=fopen("CNFUP.BAT",FOPWA)) == NULL) {
                    ipExcept->SetError(ERROR_FAIL,"interp() - Cannot create a new cnfup.bat");
                    THROW(ipExcept);
               }
               fprintf(cnfup,"%s\n",tfspst);
          }
          else if (tfspfx("CNF down:")) {
               if (cnfdn == NULL && (cnfdn=fopen("CNFDN.BAT",FOPWA)) == NULL) {
                    ipExcept->SetError(ERROR_FAIL,"interp() - Cannot create a new cnfdn.bat");
                    THROW(ipExcept);
               }
               fprintf(cnfdn,"%s\n",tfspst);
          }
          else if (tfspfx("Channel type:")) {
               n=strlen(chantyps);
               chantyps=(CHAR *)alcrsz(chantyps,n+1,n+8+1+strlen(tfspst)+1+1);
               strcat(chantyps,spr("CHANTYPE=%s\n",tfspst));
          }
          else if (tfspfx("Hardware type:")) {
               n=strlen(hdwrtyps);
               hdwrtyps=(CHAR *)alcrsz(hdwrtyps,n+1,n+8+1+strlen(tfspst)+1+1);
               strcat(hdwrtyps,spr("HDWRTYPE=%s\n",tfspst));
          }
     }
     ipExcept->Delete();
}

static VOID
wrapit(VOID)                  // wrap up scanning of an .MDF file
{
     struct lingo *lp;

     if (moddat[nmods].modnam[0] != '\0') {
          if ((lp=moddat[nmods].lp) != NULL) {
               if (lp->name[0] == '\0') {
                    free(lp);
                    moddat[nmods].lp=NULL;
               }
               else {
                    if (lp->editor[0] == '\0') {
                         strcpy(lp->editor,DFTEDR);
                    }
                    if (lp->yes[0] == '\0') {
                         strcpy(lp->yes,DFTYES);
                    }
                    if (lp->no[0] == '\0') {
                         strcpy(lp->no,DFTNO);
                    }
               }
          }
          memmove(moddat[nmods].mdfnam,tfsfb.ff_name,findstg(".",tfsfb.ff_name)-1);
          nmods++;
     }
     else {
          zapmod(nmods);
     }
}

static VOID
dwreps(VOID)                  // deal with "replaces:" chains
{
     INT i,j,k,l,done;
     CHAR *repnam;

     done=0;
     while (!done) {
          done=1;
          for (i=0 ; i < nmdfs ; i++) {
               for (j=0 ; j < MAXMDF ; j++) {
                    repnam=moddat[i].mdfrep[j];
                    if (*repnam == '\0') {
                         break;
                    }
                    for (k=0 ; k < nmdfs ; k++) {
                         if (sameas(repnam,moddat[k].mdfnam)) {
                              zapmod(k);
                         }
                         else {
                              for (l=0 ; l < MAXMDF ; l++) {
                                   if (repifs(moddat[k].mdfreq[l],
                                              moddat[i].mdfnam,repnam)) {
                                        done=0;
                                   }
                                   if (repifs(moddat[k].ineed[l],
                                              moddat[i].mdfnam,repnam)) {
                                        done=0;
                                   }
                                   if (repifs(moddat[k].needme[l],
                                              moddat[i].mdfnam,repnam)) {
                                        done=0;
                                   }
                              }
                         }
                    }
               }
          }
     }
}

static GBOOL                       //   TRUE if string was replaced
repifs(                            // sometimes replace one string with another
CHAR *target,                      //   replace this string
CHAR *source,                      //   ...with this string
CHAR *cmpstg)                      //   ...only if it currently matches this
{
     if (sameas(target,cmpstg)) {
          strcpy(target,source);
          return(TRUE);
     }
     return(FALSE);
}

static VOID
zapmod(                       // erase a module's info from moddat[] array
INT imod)
{
     if (moddat[imod].lp != NULL) {
          free(moddat[imod].lp);
     }
     if (moddat[imod].msgs != NULL) {
          free(moddat[imod].msgs);
     }
     if (moddat[imod].stfunc != NULL) {
          free(moddat[imod].stfunc);
     }
     memset(&moddat[imod],0,sizeof(struct moddat));
}

static VOID
plinfo(VOID)                               // point to language information
    // (equiv to inilingo(), but done within the world created by iniint())
{
     INT i,j;
     struct lingo *lp;

     nlingo=0;
     for (i=0 ; i < nmdfs ; i++) {
          if ((lp=moddat[i].lp) != NULL) {
               nlingo++;
               for (j=0 ; j < i ; j++) {
                    if (moddat[j].lp != NULL
                     && sameas(moddat[j].lp->name,lp->name)) {
                         free(lp);
                         moddat[i].lp=NULL;
                         nlingo--;
                         break;
                    }
               }
          }
     }
     alclng();
     languages[0]=&dftlang;
     nlingo=1;
     for (i=0 ; i < nmdfs ; i++) {
          if ((lp=moddat[i].lp) != NULL) {
               if (sameas(lp->name,languages[0]->name)) {
                    memmove(languages[0],lp,sizeof(struct lingo));
                    free(lp);
               }
               else {
                    languages[nlingo++]=lp;
               }
          }
     }
}

static INT
dupsok(VOID)                  // return 1 if no module names are duplicated
{
     INT i,j;

     for (i=0 ; i < nmdfs ; i++) {
          if (moddat[i].modnam[0] != '\0') {
               for (j=0 ; j < i ; j++) {
                    if (sameas(moddat[i].modnam,moddat[j].modnam)) {
                         resolv(i,j);
                         return(0);
                    }
               }
          }
     }
     return(1);
}

static VOID
resolv(                       // resolve a module name conflict
INT i,
INT j)
{
     cflict myDialog;
     CHAR newi[MNMSIZ];
     CHAR newj[MNMSIZ];

     CCatException* cfExcept=new CCatException;
     sprintf(newi,"%.3s_%.*s",moddat[i].mdfnam,MNMSIZ-5,moddat[i].modnam);
     sprintf(newj,"%.3s_%.*s",moddat[j].mdfnam,MNMSIZ-5,moddat[j].modnam);

     if (isService) {
          cfExcept->Delete();
          dsamdf(i);
          return;
     }
     myDialog.SetupLabels(moddat[i].mdfnam,moddat[i].modnam,moddat[i].devnam,
          moddat[j].mdfnam,moddat[j].modnam,moddat[j].devnam);
     myDialog.DoModal();

     switch(myDialog.m_choice) {
     case 0:
          dsamdf(i);
          break;
     case 1:
          renmod(i,newi);
          break;
     case 2:
          dsamdf(j);
          break;
     case 3:
          renmod(j,newj);
          break;
     case 4:
          cfExcept->SetError(ERROR_RECOVER,"resolv() - Integration check cancelled by operator");
          THROW(cfExcept);
          break;
     }
     cfExcept->Delete();
}

static VOID
dsamdf(                       // disable an .MDF file
INT i)
{
     CHAR namorg[FNSIZE+3],nammdf[FNSIZE+3];

     strcpy(namorg,moddat[i].mdfnam);
     strcat(namorg,".dmd");
     strcpy(nammdf,moddat[i].mdfnam);
     strcat(nammdf,".mdf");
     rename(nammdf,namorg);
     unlink(nammdf);
}

static VOID
renmod(                // rename a module to what's in newi
INT i,
CHAR *newi)
{
     CHAR *tmpfln;
     FILE *tmpfp,*mdffp;
     CHAR mdfbuf[100],nammdf[FNSIZE+3];

     CCatException* rmExcept=new CCatException;
     strcpy(nammdf,moddat[i].mdfnam);
     strcat(nammdf,".mdf");
     if ((tmpfp=fopen(tmpfln=tmpnam(NULL),FOPWA)) == NULL
      || (mdffp=fopen(nammdf,FOPRA)) == NULL) {
          rmExcept->SetError(ERROR_FAIL,"Fatal error in renmod()");
          THROW(rmExcept);
     }
     while (fgets(mdfbuf,sizeof(mdfbuf),mdffp) != NULL) {
          if (sameto("Module Name:",mdfbuf)) {
               sprintf(mdfbuf,"Module Name: %s\n",newi);
          }
          fputs(mdfbuf,tmpfp);
     }
     fclose(mdffp);
     fclose(tmpfp);
     dsamdf(i);
     rename(tmpfln,nammdf);
     rmExcept->Delete();
}

INT                                // return 1 if initted, 0 otherwise
refsok(VOID)                // init 'nrefs' values from wgsmdf.ref if poss
{
     FILE *efp;
     Cffblk fb;
     INT i,j;
     struct moddat refmod;

     if ((efp=fopen("wgsmdf.ref",FOPRB)) == NULL) {
          return(0);
     }
     if (!fnd1st(&fb,"wgsmenu2.dat",0)) {
          return(0);
     }
     if (fread(&refstf,sizeof(struct refstf),1,efp) != 1
       || fb.ff_ftime != refstf.fb.ff_ftime
       || fb.ff_fdate != refstf.fb.ff_fdate
       || fb.ff_fsize != refstf.fb.ff_fsize) {
          fclose(efp);
          refstf.fb.ff_handle=INVALID_HANDLE_VALUE;
          return(0);
     }
     refstf.fb.ff_handle=INVALID_HANDLE_VALUE;
     for (i=0 ; i < refstf.nmods ; i++) {
          if (fread(&refmod,sizeof(struct moddat),1,efp) != 1) {
               fclose(efp);
               return(0);
          }
          if (refmod.nrefs != 0 && !sameas(refmod.modnam,MMMODN)) {
               for (j=0 ; j < nmods ; j++) {
                    if (sameas(refmod.modnam,moddat[j].modnam)) {
                         moddat[j].nrefs=refmod.nrefs;
                         break;
                    }
               }
               if (j == nmods) {
                    addxmod();
                    memmove(&moddat[j],&refmod,sizeof(struct moddat));
                    anyUtils[j]=FALSE;
               }
          }
     }
     fclose(efp);
     return(1);
}

static VOID
addxmod(VOID)                 // add an "extra" module (not in an .MDF)
{
     UINT cursiz,newsiz;

     cursiz=nmods*sizeof(GBOOL);
     newsiz=cursiz+sizeof(GBOOL);
     anyUtils=(GBOOL *)alcrsz(anyUtils,cursiz,newsiz);
     cursiz=nmods*sizeof(struct moddat);
     newsiz=cursiz+sizeof(struct moddat);
     moddat=(struct moddat *)alcrsz(moddat,cursiz,newsiz);
     nmods++;
}

VOID
ctrefs(VOID)                  // count up references the hard way
{
     UINT reccnt;
     DFAFILE *muibb;
     struct mnupag mp;
     extern VOID unpack(struct mnupag *menupage); // in MNUTIL.C

     pMW->ShowWindow(SW_SHOWNORMAL);
     pST->SetWindowText("Examining menu tree references....");
     muibb=dfaOpen("wgsmenu2.dat",sizeof(struct mnupag),NULL);
     reccnt=(INT)dfaCountRec();
     if (reccnt > 0) {
          pPRG->SetRange(0,reccnt);
          pPRG->SetStep(1);
          pPRG->SetPos(0);
     }
     dfaGetLO(&mp,0);
     while (1) {
          unpack(&mp);
          if (mp.flags&MDLPAG) {
               incref(mp.modnam);
          }
          if (!dfaQueryNX()) {
               if (reccnt > 0) {
                    pPRG->SetPos(0);
               }
               break;
          }
          memmove(&mp,muibb->data,sizeof(struct mnupag));
          if (reccnt > 0) {
               pPRG->StepIt();
          }
     }
     dfaClose(muibb);
     dfaRstBlk();
}

static VOID
warnem(VOID)                       // warn user about dangling references
{
     INT i,j;

     if (nmods != nmdfs) {
          pMW->ShowWindow(SW_SHOWNORMAL);
          for (i=nmdfs,j=0 ; i < nmods && j < 7 ; i++,j++) {
               pST->SetWindowText(moddat[i].modnam);
          }
          nmiss++;
     }
}

static VOID
reqwarn(               // warn of missing .MDF that is required
CHAR *mdfnam)
{
     //CHAR buf[256];

     //pMW->ShowWindow(SW_SHOWNORMAL);
     if (!isService) {
          bReqWarned=TRUE;
          pRD->ShowWindow(SW_SHOWNORMAL);
          if (pRD->m_listReq.FindStringExact(-1,mdfnam) == LB_ERR) {
               pRD->m_listReq.AddString(mdfnam);
          }
     }
     //sprintf(buf,"A required file, %s.mdf is missing.",mdfnam);
     //pST->SetWindowText(buf);
}

static VOID
record(VOID)                  // records info on *.MDF and WGSMENU2.DAT
{                             //    in file WGSINT.REF
     FILE *fp;
     Cffblk dirfb;

     if ((fp=fopen("wgsint.ref",FOPWB)) != NULL) {
          if (fnd1st(&dirfb,"*.mdf",0)) {
               do {
                    fwrite(&dirfb,sizeof(Cffblk),1,fp);
               } while (fndnxt(&dirfb));
          }
          if (fnd1st(&dirfb,"wgsmenu2.dat",0)) {
               fwrite(&dirfb,sizeof(Cffblk),1,fp);
          }
          fclose(fp);
          copyfile("wgsint.ref","wgsintcs.ref");
     }
}

static VOID
rfrapp(VOID)
{
     FILE *fp;
     Cffblk oldfb;
     Cffblk newfb;
     struct moddat *modptr;
     CHAR appid[AIDSIZ];
     size_t i;

     pMW->ShowWindow(SW_SHOWNORMAL);
     if ((fp=fopen("wgsintcs.ref",FOPRB)) == NULL) {
          pST->SetWindowText("Building support file list....");
          zapapps();
     }
     else {
          while (fread(&oldfb,sizeof(Cffblk),1,fp) == 1
            && !sameas(oldfb.ff_name,"wgsmenu2.dat")) {
               oldfb.ff_handle=INVALID_HANDLE_VALUE;
               stzcpy(appid,oldfb.ff_name,AIDSIZ);
               strtok(appid,".");
               if (fnd1st(&newfb,oldfb.ff_name,0)) {
                    if ((modptr=fndapp(appid)) == NULL) {
                         zapapp(appid);
                    }
                    else {
                         modptr->flags|=ISINRF;
                         if (oldfb.ff_ftime != newfb.ff_ftime
                           || oldfb.ff_fdate != newfb.ff_fdate
                           || oldfb.ff_fsize != newfb.ff_fsize) {
                              zapapp(appid);
                         }
                         if (modptr->flags&HASCLX) {
                              addapp(appid);
                         }
                    }
               }
               else {
                    zapapp(appid);
               }
          }
          oldfb.ff_handle=INVALID_HANDLE_VALUE;
          fclose(fp);
     }
     for (i=0,modptr=moddat ; i < (size_t)nmdfs ; i++,modptr++) {
          if ((modptr->flags&HASCLX) && !(modptr->flags&ISINRF)) {
               zapapp(modptr->mdfnam);
               addapp(modptr->mdfnam);
          }
     }
}

static VOID
zapapps(VOID)
{
     struct saunam zapper;

     memset(&zapper,0,sizeof(struct saunam));
     stzcpy(zapper.appid,mmappid,AIDSIZ);
     zapper.flags=0;
     stzcpy(zapper.suffix,"appinfo ",SFXSIZ);
     zappem(&zapper);
     zapper.flags=FLGFIL;
     stzcpy(zapper.suffix,"appfile ",SFXSIZ);
     zappem(&zapper);
     stzcpy(zapper.suffix,"ssfinf ",SFXSIZ);
     zappem(&zapper);
     stzcpy(zapper.suffix,"ssfapp ",SFXSIZ);
     zappem(&zapper);
}

static VOID
ssfapp(VOID)
{
     Cffblk fb;

     while (fnd1st(&fb,"GALNSSF\\*.*",0)) {
          bestssf("GALNSSF",fb.ff_name);
     }
     rmdir("GALNSSF");
}

static VOID
zapapp(
CHAR *appid)
{
     struct saunam zapper;

     memset(&zapper,0,sizeof(struct saunam));
     stzcpy(zapper.appid,mmappid,AIDSIZ);
     zapper.flags=0;
     stzcpy(zapper.suffix,spr("appinfo %s",appid),SFXSIZ);
     writegdp(&zapper,0,NULL,NULL);
     zapper.flags=FLGFIL;
     stzcpy(zapper.suffix,spr("appfile %s ",appid),SFXSIZ);
     zappem(&zapper);
     stzcpy(zapper.suffix,spr("ssfinf %s ",appid),SFXSIZ);
     zappem(&zapper);
     stzcpy(zapper.suffix,spr("ssfapp %s ",appid),SFXSIZ);
     zappem(&zapper);
}

static VOID
zappem(
struct saunam *szap)
{
     CHAR minmat[SFXSIZ];
     CHAR junk;

     stzcpy(minmat,szap->suffix,SFXSIZ);
     while (readgdpGT(szap,sizeof(junk),&junk,minmat,NULL)) {
          writegdp(szap,0,NULL,NULL);
     }
}

static VOID
addapp(
CHAR *appid)
{
     INT nsupfs,ssupfs,oldsf,noldsf;
     CHAR appver[VERSIZ],*mp;
     CHAR appistg[MNMSIZ+VERSIZ+6];
     CHAR tmp[256];
     GBOOL isblank;
     CHAR *sfls;
     static struct saunam adder;

     CCatException* aaExcept=new CCatException;
     if (tfsopn(spr("%s.mdf",appid)) != 1) {
     //     aaExcept->SetError(ERROR_FAIL,"Fatal error in addapp() - %s.mdf",appid);
     //     THROW(aaExcept);
          return;   // Gracefully return instead of blowing up.
     }

     pMW->ShowWindow(SW_SHOWNORMAL);
     sprintf(tmp,"Parsing Module Definition File: %s",appid);
     pST->SetWindowText(tmp);
     strcpy(appver,"0.0");
     memset(&adder,0,sizeof(struct saunam));
     stzcpy(adder.appid,mmappid,AIDSIZ);
     stzcpy(adder.suffix,spr("appinfo %s",appid),SFXSIZ);
     if (readgdpEQ(&adder,0,NULL,NULL)) {
          aaExcept->Delete();
          return;
     }
     adder.flags=FLGFIL;
     nsupfs=ssupfs=0;
     isblank=FALSE;
     while (tfsrdl() != TFSDUN) {
          switch (tfstate) {
          case TFSLIN:
               if (tfspfx("Client app version:")) {
                    stzcpy(appver,tfspst,VERSIZ);
               }
               else if (tfspfx("Client app EXE:")) {
                    stzcpy(adder.suffix,spr("appfile %s EXE",appid),SFXSIZ);
                    writegdp(&adder,STGLEN,spr("%s\\%s",appid,tfspst),NULL);
               }
               else if (tfspfx("Client app support files:")
                  && (mp=strtok(tfspst,DELIMS)) != NULL) {
                    do {
                         noldsf=itemcntd(OLDSSFLS," ");
                         for (oldsf=0 ; oldsf < noldsf ; oldsf++) {
                              if (sameas(mp,itemidxd(OLDSSFLS,oldsf," "))) {
                                   break;
                              }
                         }
                         if (oldsf == noldsf) {
                              stzcpy(adder.suffix,
                                     spr("appfile %s %d",appid,nsupfs),SFXSIZ);
                              nsupfs++;
                              writegdp(&adder,STGLEN,spr("%s\\%s",appid,mp),NULL);
                         }
                    } while ((mp=strtok(NULL,DELIMS)) != NULL);
               }
               else if (sameto("Shared support files:",tfsbuf)) {
                    if (tfspfx("Shared support files:")) {
                         ssupfs=addssfs(ssupfs,appid,tfspst,&adder);
                         if (ssupfs > 0) {
                              isblank=FALSE;
                         }
                    }
                    else if (ssupfs == 0) {
                         isblank=TRUE;
                    }
               }
               break;
          }
     }
     if (!isblank && ssupfs == 0) {
          sfls=alcdup(OLDSSFLS);
          ssupfs=addssfs(ssupfs,appid,sfls,&adder);
          free(sfls);
     }
     adder.flags=0;
     stzcpy(adder.suffix,spr("appinfo %s",appid),SFXSIZ);
     sprintf(appistg,"%s\n%s\n%d\n%d",fndapp(appid)->modnam,
                                      appver,
                                      nsupfs,
                                      ssupfs);
     writegdp(&adder,STGLEN,appistg,NULL);
     aaExcept->Delete();
}

static INT
addssfs(
INT ssupfs,
CHAR *appid,
CHAR *files,        // WARNING: content of "files" modified by strtok()
struct saunam *adder)
{
     struct dpkwv inf;
     CHAR *mp;

     if ((mp=strtok(files,DELIMS)) != NULL) {
          do {
               stzcpy(adder->suffix,spr("ssfinf %s %d",appid,ssupfs),SFXSIZ);
               ssupfs++;
               writegdp(adder,STGLEN,mp,NULL);
               memset(&inf,0,sizeof(struct dpkwv));
               stzcpy(inf.filnam,spr("%s\\%s",appid,mp),GCMAXPTH);
               if (!getwinver(&inf)) {
                    stzcpy(inf.filnam,spr("%s\\%s",SSFDIR,mp),GCMAXPTH);
                    getwinver(&inf);
               }
               else {
                    bestssf(appid,mp);
               }
               stzcpy(adder->suffix,spr("ssfapp %s %s",appid,mp),SFXSIZ);
               writegdp(adder,sizeof(struct dpkwv),&inf,dpkwvFDA);
          } while ((mp=strtok(NULL,DELIMS)) != NULL);
     }
     return(ssupfs);
}

static struct moddat *
fndapp(
CHAR *appid)
{
     INT i;

     for (i=0 ; i < nmdfs ; i++) {
          if (sameas(moddat[i].mdfnam,appid)) {
               return(&moddat[i]);
          }
     }
     return(NULL);
}

static VOID
opncsf(VOID)
{
     if (!csfopend) {
          opngdp();
          mmappid=strdup(msgscan("wgserver.msg","MMAID1"));
          csfopend=TRUE;
     }
}

static VOID
clscsf(VOID)
{
     if (csfopend) {
          clsgdp();
          csfopend=FALSE;
     }
}

CHAR *
gtmdfnam(                          // given module name, return mdf name
CHAR *appname)
{
     INT i;

     for (i=0 ; i < nmdfs ; i++) {
          if (sameas(moddat[i].modnam,appname)) {
               return(moddat[i].mdfnam);
          }
     }
     return("");
}

VOID
markfile(                          // mark file with WGSMENU2.DAT's Cffblk
CHAR *filname,
Cffblk *fb,
GBOOL fromend)
{
     FILE *fp;

     if ((fp=fopen(filname,FOPRWB)) != NULL) {
          fseek(fp,fromend ? -((LONG)sizeof(Cffblk)) : 0L,
                   fromend ? SEEK_END : SEEK_SET);
          fwrite(fb,sizeof(Cffblk),1,fp);
          fclose(fp);
     }
}

CHAR *
gmdnam(                            // get a module's name from .MDF file
CHAR *mdfnam)                          // name of module's .MDF file
{
     FILE *fp;
     static CHAR tmpbuf[40];

     CCatException* gnExcept=new CCatException;
     if ((fp=fopen(mdfnam,FOPRA)) == NULL) {
          return("");              // a nicer return
     }
     while (fgets(tmpbuf,sizeof(tmpbuf),fp) != NULL) {
          if (sameto("Module Name:",tmpbuf)) {
               unpad(tmpbuf);
               fclose(fp);
               gnExcept->Delete();
               return(skpwht(tmpbuf+12));
          }
     }
     gnExcept->SetError(ERROR_FAIL,"Fatal error in gmdnam() - No module name in \"%s\"",mdfnam);
     THROW(gnExcept);
     return(tmpbuf);
}

 static VOID
 bestssf(                           // move in best support file
 CHAR *appid,
 CHAR *filename)
 {
      struct dpkwv cur,old;
      Cffblk fb;
      GBOOL exists;

      if (!sameas(appid,SSFDIR)) {
           if (!sameas(appid,"GALNSSF")) {
                sprintf(cur.filnam,"GALNSSF\\%s",filename);
                exists=fnd1st(&fb,cur.filnam,0);
           }
           else {
                exists=FALSE;
           }
           do {
                sprintf(cur.filnam,"%s\\%s",appid,filename);
                sprintf(old.filnam,"%s\\%s",SSFDIR,filename);
                if (getwinver(&cur)) {
                     if (getwinver(&old) && wvcompare(&old,&cur) <= 0) {
                          unlink(cur.filnam);
                     }
                     else {
                          unlink(old.filnam);
                          mkdir(SSFDIR);
                          rename(cur.filnam,old.filnam);
                     }
                }
                if (exists) {
                     appid="GALNSSF";
                     exists=FALSE;
                }
                else {
                     break;
                }
           } while(1);
      }
 }

 static VOID
 ssfiler(VOID)                      // explore shared support files
 {
      struct ssfcache {
           CHAR sfx[SFXSIZ];
           CHAR fnm[FNEXSZ];
           struct dpkwv dpkwv;
           struct ssfcache *next;
      } *head,*cur,*prv,*tmp;
      struct dpkwv inf;
      Cffblk fb;
      struct saunam genam;
      CHAR minmat[SFXSIZ];

      pMW->ShowWindow(SW_SHOWNORMAL);
      pST->SetWindowText("Examining Shared Support Files....");
      memset(&genam,0,sizeof(struct saunam));
      stzcpy(genam.appid,mmappid,AIDSIZ);
      genam.flags=FLGFIL;
      stzcpy(minmat,"ssfile ",SFXSIZ);
      stzcpy(genam.suffix,minmat,SFXSIZ);
      head=NULL;
      while (readgdpGT(&genam,sizeof(struct dpkwv),&inf,minmat,dpkwvFDA)) {
           cur=(struct ssfcache *)alczer(sizeof(struct ssfcache));
           stzcpy(cur->sfx,genam.suffix,SFXSIZ);
           stzcpy(cur->fnm,firstwd(skpwht(skpwrd(genam.suffix))),FNEXSZ);
           cur->dpkwv=inf;
           cur->next=head;
           head=cur;
      }
      if (fnd1st(&fb,SSFDIR"\\*.*",0)) {
           do {
                for (cur=head ; cur != NULL ; cur=cur->next) {
                     if (sameas(cur->fnm,fb.ff_name)) {
                          break;
                     }
                }
                sprintf(minmat,"ssfile %s",fb.ff_name);
                if (cur == NULL
                 || cur->dpkwv.size != (ULONG)fb.ff_fsize
                 || cur->dpkwv.date != fb.ff_fdate
                 || cur->dpkwv.time != fb.ff_ftime
                 || !sameas(minmat,cur->sfx)) {
                     memset(inf.filnam,0,GCMAXPTH);
                     sprintf(inf.filnam,SSFDIR"\\%s",fb.ff_name);
                     inf.date=fb.ff_fdate;
                     inf.time=fb.ff_ftime;
                     inf.size=fb.ff_fsize;
                     getwinver(&inf);
                     stzcpy(genam.suffix,minmat,SFXSIZ);
                     writegdp(&genam,sizeof(struct dpkwv),&inf,dpkwvFDA);
                }
                if (cur != NULL) {
                     prv=NULL;
                     cur=head;
                     while (cur != NULL) {
                          if (sameas(minmat,cur->sfx)) {
                               if (prv != NULL) {
                                    prv->next=cur->next;
                                    tmp=cur;
                                    cur=cur->next;
                               }
                               else {
                                    head=cur->next;
                                    tmp=cur;
                                    cur=head;
                               }
                               free(tmp);
                          }
                          else {
                               prv=cur;
                               cur=cur->next;
                          }
                     }
                }
           } while (fndnxt(&fb));
      }
      while (head != NULL) { // delete all remaining records
           stzcpy(genam.suffix,head->sfx,SFXSIZ);
           writegdp(&genam,0,NULL,NULL);
           cur=head;
           head=head->next;
           free(cur);
      }
 }
