/***************************************************************************
 *                                                                         *
 *   INTEGROU.C                                                            *
 *                                                                         *
 *   Copyright (c) 1992-1997 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 "gcomm.h"
#include "majorbbs.h"
#include "integrou.h"
#include "gcspsrv.h"
#include "gcwinver.h"
#include "galnssf.h"

#define FILREV "$Revision: 30 $"

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                 */

#if defined( GCDOSL )
unsigned _stklen=8*1024;           /* set large model stack to 8K          */
#endif

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

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

static VOID updbtr(VOID);
static VOID wrtact(VOID);
static VOID wrtmdf(VOID);
static VOID wrtutl(VOID);
static VOID tagreq(struct moddat *mddptr);
static VOID adddll(CHAR **dllist,CHAR *dllnam);
static VOID bldmda(VOID);
static VOID interp(VOID);
static VOID wrapit(VOID);
static VOID dwreps(VOID);
static GBOOL repifs(CHAR *target,CHAR *source,CHAR *cmpstg);
static VOID zapmod(INT imod);
static VOID plinfo(VOID);
static INT dupsok(VOID);
static VOID resolv(INT i,INT j);
static GBOOL validch(CHAR c,CHAR *stg);
static VOID dsamdf(INT i);
static VOID renmod(INT i,CHAR *newi);
static VOID addxmod(VOID);
static VOID warnem(VOID);
static VOID reqwarn(CHAR *mdfnam);
static VOID w4morc(VOID);
static VOID record(VOID);

static VOID ssfapp(VOID);
static VOID rfrapp(VOID);
static VOID zapapps(VOID);
static VOID zapapp(CHAR *appid);
static VOID zappem(struct saunam *szap);
static VOID addapp(CHAR *appid);
static INT addssfs(INT ssupfs,CHAR *appid,CHAR *files,struct saunam *adder);
static struct moddat *fndapp(CHAR *appid);
static VOID opncsf(VOID);
static VOID clscsf(VOID);
static VOID bestssf(CHAR *appid,CHAR *filnam);
static VOID ssfiler(VOID);

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;
          setmem(&moddat[nmods-1],sizeof(struct moddat),0);
          stzcpy(moddat[nmods-1].modnam,modnam,MNMSIZ);
          moddat[nmods-1].nrefs=1;
     }
}

VOID
decref(                       /* decrement 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)) {
                    if (moddat[i].nrefs-- == 0) {
                         catastro("DECREF: \"%s\" < 0",modnam);
                    }
                    return;
               }
          }
          catastro("DECREF: NO \"%s\"",modnam);
     }
}

VOID
finint(VOID)        /* write WGSERV.CFG, WGSMDF.REF, WGSINT.REF, WGSMDFS.LST*/
{                   /* and WGSBTR.BAT (call only once per iniint())        */
     reqchk();
     wrtact();
     wrtmjr();
     wrtmdf();
     wrtutl();
#ifdef UNIX
     wrtmod();
#endif // UNIX
     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();
     updbtr();
}

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

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

     if ((fp=fopen("wgsmdfs.lst",FOPWA)) == NULL) {
          catastro("WRTACT: CAN'T OPEN \"wgsmdfs.lst\"");
     }
     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);
}

static VOID
tagreq(mddptr)                /* 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++;
               }
          }
     }
}

#ifdef UNIX
VOID
wrtmod(VOID)
{
     INT i,j;
     CHAR **ifnlist;
     CHAR **objlist;
     FILE *mfp;

     ifnlist=(CHAR **)alczer((nmods*MAXDLL+1)*sizeof(CHAR *));
     objlist=(CHAR **)alczer((nmods*MAXDLL+1)*sizeof(CHAR *));
     for (i=0 ; i < nmdfs ; i++) {
          if (moddat[i].flags&REQUIR) {
               for (j=0 ; j < MAXDLL ; j++) {
                    adddll(ifnlist,moddat[i].ifnnam[j]);
                    adddll(objlist,moddat[i].objlst[j]);
               }
          }
     }
     if ((mfp=fopen("module.c",FOPWA)) == NULL) {
       catastro("Can't create \"module.c\"");
     }
     fprintf(mfp,"void\ncallinits()\n{\n");
     for (i=0 ; ifnlist[i] != NULL ; i++) {
          fprintf(mfp,"     %s;\n",ifnlist[i]);
     }
     fprintf(mfp,"}\n");
     fclose(mfp);
     if ((mfp=fopen("ObjectFileList",FOPWA)) == NULL) {
       catastro("Can't create \"ObjectFileList\"");
     }
     for (i=0 ; objlist[i] != NULL ; i++) {
          fprintf(mfp,"%s\n",objlist[i]);
     }
     fclose(mfp);
     free(objlist);
     free(ifnlist);
}
#endif // UNIX

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;

     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) {
          catastro("CAN'T CREATE \"wgserv.cfg\"");
     }
     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);
}

static VOID
adddll(dllist,dllnam)         /* add a dll name to the dll list            */
CHAR **dllist,*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;

     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++) {
                    movmem(&moddat[i],&dskdat,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;

     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
updbtr(VOID)                  /* update WGSBTR.BAT with page & file parms  */
{                             /*   (don't cata if WGSBTR.BAT is read-only) */
#ifdef GCDOS
     FILE *fp;
     INT recsiz=-1;
     CHAR *tfsrch="Record size";

     if (tfsopn("wgsbreq.ini") == 1) {
          while (tfsrdl() != TFSDUN) {
               if (tfstate == TFSLIN) {
                    if (tfspfx(tfsrch)) {
                         if (!isdigit(*tfspst)) {
                              tfspst++;
                         }
                         recsiz=atoi(tfspst);
                    }
               }
          }
     }
     if ((fp=fopen("wgsbtr.bat",FOPWA)) != NULL) {
          if (recsiz > 0) {
               fprintf(fp,"BREQUEST /D:%d >NUL\n",recsiz);
          }
          else {
               fprintf(fp,"BTRIEVE /P:4096 /F:%u /M:64 /E >NUL\n",
                                             btrtfl+10);
          }
          fclose(fp);
     }
#endif // GCDOS
}

static VOID
bldmda(VOID)                  /* build the moddat array from .MDF files    */
{
     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;

     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);
          }
#ifdef UNIX
          else if (tfspfx("Object File List:")
             && (mp=strtok(tfspst,DELIMS)) != NULL) {
               for (i=0 ; i < MAXDLL ; i++) {
                    stzcpy(moddat[nmods].objlst[i],mp,OBJSIZ);
                    if ((mp=strtok(NULL,DELIMS)) == NULL) {
                         break;
                    }
               }
          }
          else if (tfspfx("Init Functions:")
             && (mp=strtok(tfspst,DELIMS)) != NULL) {
               for (i=0 ; i < MAXDLL ; i++) {
                    stzcpy(moddat[nmods].ifnnam[i],mp,FUNCSZ);
                    if ((mp=strtok(NULL,DELIMS)) == NULL) {
                         break;
                    }
               }
          }
#endif                             // UNIX
          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);
          }
#ifdef GCDOS
          else if (tfspfx("Btrieve files:")
                || tfspfx("Dynamic Btrieve files:")) {
               moddat[nmods].btrfls+=atoi(tfspst);
          }
#endif // GCDOS
          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=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) {
                    catastro("Cannot create a new wgsup.bat");
               }
               fprintf(bbsup,"%s\n",tfspst);
          }
          else if (tfspfx("WGS down:")) {
               if (bbsdn == NULL && (bbsdn=fopen("wgsdn.bat",FOPWA)) == NULL) {
                    catastro("Cannot create a new wgsdn.bat");
               }
               fprintf(bbsdn,"%s\n",tfspst);
          }
          else if (tfspfx("CNF up:")) {
               if (cnfup == NULL && (cnfup=fopen("CNFUP.BAT",FOPWA)) == NULL) {
                    catastro("Cannot create a new CNFUP.BAT");
               }
               fprintf(cnfup,"%s\n",tfspst);
          }
          else if (tfspfx("CNF down:")) {
               if (cnfdn == NULL && (cnfdn=fopen("CNFDN.BAT",FOPWA)) == NULL) {
                    catastro("Cannot create a new CNFDN.BAT");
               }
               fprintf(cnfdn,"%s\n",tfspst);
          }
          else if (tfspfx("Channel type:")) {
               n=strlen(chantyps);
               chantyps=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=alcrsz(hdwrtyps,n+1,n+8+1+strlen(tfspst)+1+1);
               strcat(hdwrtyps,spr("HDWRTYPE=%s\n",tfspst));
          }
     }
}

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);
                    }
               }
          }
          movmem(tfsfb.ff_name,moddat[nmods].mdfnam,
                 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(INT imod)              /* erase a module's info from moddat[] array */
{
     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);
     }
     setmem(&moddat[imod],sizeof(struct moddat),0);
}

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)) {
                    movmem(lp,languages[0],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(i,j)                   /* resolve a module name conflict            */
INT i,j;
{
     CHAR *scnsav,*integs;
     CHAR newi[MNMSIZ],newj[MNMSIZ];
     static CHAR choice[2]="";
     INT savex,savey;

     savex=curcurx();
     savey=curcury();
     scn2mem(scnsav=alcmem(GVIDSCNSIZ),0,GVIDSCNSIZ);
     iniscn("wgsint.scn",integs=alcmem(GVIDSCNSIZ));
     explodeto(integs,0,0,46,22,(80-46)/2,1);
     setatr(0x4E);
     prat(21,5,"%s\"",moddat[i].modnam);
     prat(20,6,"%s?",moddat[i].devnam);
     prat(20,8,"%s\"",moddat[i].modnam);
     prat(20,9,"%s",moddat[i].devnam);
     sprintf(newi,"%.3s_%.*s",moddat[i].mdfnam,MNMSIZ-5,moddat[i].modnam);
     prat(12,10,"%s\"?",newi);
     prat(21,12,"%s\"",moddat[j].modnam);
     prat(20,13,"%s?",moddat[j].devnam);
     prat(20,15,"%s\"",moddat[j].modnam);
     prat(20,16,"%s",moddat[j].devnam);
     sprintf(newj,"%.3s_%.*s",moddat[j].mdfnam,MNMSIZ-5,moddat[j].modnam);
     prat(12,17,"%s\"?",newj);
     setmem(choice,sizeof(choice),0);
     do {
          edtval(29,21,sizeof(choice),choice,(GBOOL (*)())validch,USEPOFF);
     } while (choice[0] == 0 && printf("\7"));
     mem2scn(scnsav,0,GVIDSCNSIZ);
     locate(savex,savey);
     free(scnsav);
     free(integs);
     switch (choice[0]) {
     case '1':
          dsamdf(i);
          break;
     case '2':
          renmod(i,newi);
          break;
     case '3':
          dsamdf(j);
          break;
     case '4':
          renmod(j,newj);
          break;
     case '5':
          cataexit();
          break;
     }
}

static GBOOL
validch(                      /* edtval() validation routine for resolv()  */
CHAR c,
CHAR *stg)
{
     if (c >= '1' && c <= '5') {
          *stg=c;
     }
     return(FALSE);
}

static VOID
dsamdf(i)                     /* 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(i,newi)                /* rename a module to what's in newi         */
INT i;
CHAR *newi;
{
     CHAR *tmpfln;
     FILE *tmpfp,*mdffp;
     CHAR mdfbuf[100],nammdf[FNSIZE+3];

     strcpy(nammdf,moddat[i].mdfnam);
     strcat(nammdf,".mdf");
     if ((tmpfp=fopen(tmpfln=tmpnam(NULL),FOPWA)) == NULL
      || (mdffp=fopen(nammdf,FOPRA)) == NULL) {
          catastro("RENMOD ERROR");
     }
     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);
}

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

     if ((efp=fopen("wgsmdf.ref",FOPRB)) == NULL) {
          return(0);
     }
     if (!fndfile(&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);
          return(0);
     }
     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();
                    movmem(&refmod,&moddat[j],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          */
{
     DFAFILE *muibb;
     struct mnupag mp;
     CHAR *scnsav=NULL,*integs;
     extern VOID unpack(struct mnupag *menupage); /* in WGSMTREE.C */

     muibb=dfaOpen("wgsmenu2.dat",sizeof(struct mnupag),NULL);
     if (dfaCountRec() > 30) {
          scn2mem(scnsav=alcmem(GVIDSCNSIZ),0,GVIDSCNSIZ);
          iniscn("wgsint.scn",integs=alcmem(GVIDSCNSIZ));
          explodeto(integs,48,15,79,18,(80-32)/2,10);
     }
     dfaGetLO(&mp,0);
     while (1) {
          unpack(&mp);
          if (mp.flags&MDLPAG) {
               incref(mp.modnam);
          }
          if (!dfaQueryNX()) {
               break;
          }
          movmem(muibb->data,&mp,sizeof(struct mnupag));
     }
     dfaClose(muibb);
     dfaRstBlk();
     if (scnsav != NULL) {
          mem2scn(scnsav,0,GVIDSCNSIZ);
          free(scnsav);
          free(integs);
     }
}

static VOID
warnem(VOID)                  /* warn user about dangling references       */
{
     INT i,j;
     CHAR *scnsav,*integs;
     INT savex,savey;

     if (nmods != nmdfs) {
          savex=curcurx();
          savey=curcury();
          scn2mem(scnsav=alcmem(GVIDSCNSIZ),0,GVIDSCNSIZ);
          iniscn("wgsint.scn",integs=alcmem(GVIDSCNSIZ));
          explodeto(integs,48,0,79,14,(80-32)/2,5);
          setatr(0x4F);
          for (i=nmdfs,j=0 ; i < nmods && j < 7 ; i++,j++) {
               prat(4,j+7,"%s",moddat[i].modnam);
          }
          cursiz(GVIDNOCURS);
          w4morc();
          rstcur();
          mem2scn(scnsav,0,GVIDSCNSIZ);
          locate(savex,savey);
          free(scnsav);
          free(integs);
          nmiss++;
     }
}

static VOID
reqwarn(mdfnam)               /* warn of missing .MDF that is required     */
CHAR *mdfnam;
{
     CHAR *scnsav,*integs;
     INT savex,savey;

     savex=curcurx();
     savey=curcury();
     scn2mem(scnsav=alcmem(GVIDSCNSIZ),0,GVIDSCNSIZ);
     iniscn("wgsint.scn",integs=alcmem(GVIDSCNSIZ));
     explodeto(integs,48,19,79,24,(80-32)/2,9);
     setatr(0x4E);
     prat(12,1,"%s.mdf is",mdfnam);
     cursiz(GVIDNOCURS);
     w4morc();
     rstcur();
     mem2scn(scnsav,0,GVIDSCNSIZ);
     locate(savex,savey);
     free(scnsav);
     free(integs);
}

static VOID
w4morc(VOID)                  /* wait for 1 minute, or a keyboard char     */
{
     INT startime,nowtime;
#ifdef UNIX
     CHAR *bbsdisp;

     if ((bbsdisp=getoption("BBSDISP")) != NULL
      && sameas(bbsdisp,"nullcon")) {
          return;
     }
#endif
     startime=now();
     while (1) {
          if (kbhit()) {
               getchc();
               break;
          }
          nowtime=now();
          if ((nowtime&0x1F) == (startime&0x1F)
            && (nowtime&~0x1F) != (startime&~0x1F)) {
               break;
          }
     }
}

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

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

static VOID
rfrapp(VOID)
{
     FILE *fp;
     struct ffblk oldfb,newfb;
     struct moddat *modptr;
     CHAR appid[AIDSIZ],*scnsav=NULL,*integs;
     INT i;
     CHAR msg1[]="Building support file list.";

     if ((fp=fopen("wgsintcs.ref",FOPRB)) == NULL) {
          scn2mem(scnsav=alcmem(GVIDSCNSIZ),0,GVIDSCNSIZ);
          iniscn("wgsint.scn",integs=alcmem(GVIDSCNSIZ));
          for (i=0 ; i < strlen(msg1) ; i++) {
               integs[(16*80+51+i)*2]=msg1[i];
          }
          explodeto(integs,48,15,79,18,(80-32)/2,10);
          zapapps();
     }
     else {
          while (fread(&oldfb,sizeof(struct ffblk),1,fp) == 1
            && !sameas(oldfb.ff_name,"wgsmenu2.dat")) {
               stlcpy(appid,oldfb.ff_name,AIDSIZ);
               strtok(appid,".");
               if (fndfile(&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);
               }
          }
          fclose(fp);
     }
     for (i=0,modptr=moddat ; i < nmdfs ; i++,modptr++) {
          if ((modptr->flags&HASCLX) && !(modptr->flags&ISINRF)) {
               zapapp(modptr->mdfnam);
               addapp(modptr->mdfnam);
          }
     }
     if (scnsav != NULL) {
          mem2scn(scnsav,0,GVIDSCNSIZ);
          free(scnsav);
          free(integs);
     }
}

static VOID
zapapps(VOID)
{
     struct saunam zapper;

     setmem(&zapper,sizeof(struct saunam),0);
     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)
{
     struct ffblk fb;

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

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

     setmem(&zapper,sizeof(struct saunam),0);
     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;

     stlcpy(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];
     GBOOL isblank;
     CHAR *sfls;
     static struct saunam adder;

     if (tfsopn(spr("%s.mdf",appid)) != 1) {
          /* catastro("ADDAPP: %s.MDF ERROR",appid); */
          return;             /* a little more friendly than a catastro */
     }
     strcpy(appver,"0.0");
     setmem(&adder,sizeof(struct saunam),0);
     stzcpy(adder.appid,mmappid,AIDSIZ);
     stzcpy(adder.suffix,spr("appinfo %s",appid),SFXSIZ);
     if (readgdpEQ(&adder,0,NULL,NULL)) {
          return;
     }
     adder.flags=FLGFIL;
     nsupfs=ssupfs=0;
     isblank=FALSE;
     while (tfsrdl() != TFSDUN) {
          switch (tfstate) {
          case TFSLIN:
               if (tfspfx("Client app version:")) {
                    stlcpy(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);
}

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);
               setmem(&inf,sizeof(struct dpkwv),0);
               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 ffblk */
CHAR *filname,
struct ffblk *fb,
GBOOL fromend)
{
     FILE *fp;

     if ((fp=fopen(filname,FOPRWB)) != NULL) {
          fseek(fp,fromend ? sizeof(struct ffblk)*-1L : 0L,
                   fromend ? SEEK_END : SEEK_SET);
          fwrite(fb,sizeof(struct ffblk),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];

     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);
               return(skpwht(tmpbuf+12));
          }
     }
     catastro("GMDNAM: NO MODULE NAME IN \"%s\"",mdfnam);
     return(tmpbuf);
}

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

      if (!sameas(appid,SSFDIR)) {
           if (!sameas(appid,"GALNSSF")) {
                sprintf(cur.filnam,"GALNSSF\\%s",filename);
                exists=isfile(cur.filnam);
           }
           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;
      struct ffblk fb;
      struct saunam genam;
      CHAR minmat[SFXSIZ];

      setmem(&genam,sizeof(struct saunam),0);
      stlcpy(genam.appid,mmappid,AIDSIZ);
      genam.flags=FLGFIL;
      stlcpy(minmat,"ssfile ",SFXSIZ);
      stlcpy(genam.suffix,minmat,SFXSIZ);
      head=NULL;
      while (readgdpGT(&genam,sizeof(struct dpkwv),&inf,minmat,dpkwvFDA)) {
           cur=(struct ssfcache *)alczer(sizeof(struct ssfcache));
           stlcpy(cur->sfx,genam.suffix,SFXSIZ);
           stlcpy(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 != fb.ff_fsize
                 || cur->dpkwv.date != fb.ff_fdate
                 || cur->dpkwv.time != fb.ff_ftime
                 || !sameas(minmat,cur->sfx)) {
                     setmem(inf.filnam,GCMAXPTH,0);
                     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);
      }
 }
