/***************************************************************************
 *                                                                         *
 *   BBSMAINM.C                                                            *
 *                                                                         *
 *   Copyright (c) 1993 Consensus Systems, Inc.  All rights reserved.      *
 *   Copyright (c) 1993-1997 Galacticomm, Inc.  All rights reserved.       *
 *                                                                         *
 *   This is the Galacticomm Client/Server main menu agent.                *
 *                                                                         *
 *                                              - T. Stryker 11/26/93      *
 *                                                C. Robert                *
 *                                                B. Love                  *
 *                                                D. Pitchford 6/9/94      *
 *                                                                         *
 ***************************************************************************/

#ifdef GCWINNT
#include <windows.h>
#endif
#include "gcomm.h"
#include "majorbbs.h"
#include "gcspsrv.h"
#include "wgsmm.h"
#include "wgsmainm.h"
#include "gcwinver.h"

#define FILREV "$Revision: 51 $"

static VOID mmuread(INT direction,struct saunam *dpknam);
static VOID dpkmenu(INT direction);
static VOID dpkgofind(INT direction);
static VOID dpkstart(INT direction);
static VOID dpkiconf(INT direction);
static VOID dpkhpsf(INT direction);
static VOID dpkfpage(VOID);
static VOID dpkexefl(GBOOL cmp);
static VOID dpksupfl(INT direction,GBOOL cmp);
static VOID dpksupfs(VOID);
static VOID dpkappif(INT direction);
static VOID dpkappth(VOID);
static VOID dpkrecnt(VOID);
static VOID dpkusron(VOID);
static VOID dontzap(VOID);
static VOID sendpage(CHAR *override);
static CHAR *autosel(struct mnupag *autmnu);
static GBOOL hasaxs(struct mnupag *page);
static VOID startups(VOID);
static VOID setstfunc(CHAR *dllnfunc,GBOOL (**stfunc)(VOID));
static VOID loadhpsl(VOID);
static CHAR *gethps(CHAR *pagnam);
static USHORT filtpage(CHAR *page,USHORT totlen);
static VOID makflist(CHAR *fils);
static VOID eatpage(CHAR *page);
static GBOOL eatobj(CHAR *page);
static VOID dpkhpsmenu(VOID);
static VOID mmuwrite(struct saunam *dpknam,USHORT length,VOID *value);
static VOID mmuxdone(VOID);
static VOID mmuabort(VOID);
static VOID invis(GBOOL inv);
static VOID pagestat(GBOOL ison,GBOOL isok);
static VOID pageuser(CHAR *uid,CHAR *msg);
static INT pagcsusr(CHAR *from,CHAR *msg);
static VOID gappnams(VOID);
static CHAR *appid(CHAR *modnam);
static VOID announce(INT direction);
static GBOOL annvec(VOID);
static VOID annvecyc(VOID);
static VOID dpkbkgdf(VOID);
static VOID dpksplaf(CHAR *filename);
static VOID dpklaunchpg(VOID);
static VOID dpkssfile(GBOOL cmp);
static VOID dpkssfinf(INT direction);
static INT getnsupfs(CHAR *data);
static struct dpkwv *getssfinf(CHAR *filnam);
static CHAR *compext(CHAR *filnam);
static VOID pagecon(VOID);
static VOID reg_dpk(VOID);
static VOID BuildKeys(VOID);

#define ICONDIR  "wgsicons"SLS     /* local directory for icon files       */
#define ICONEXT  ".ico"            /* extension for icon files             */
#define DEFICO   "wgsdef"          /* default icon file name               */
#define IMGICO   "wgsimage"        /* default system icon file name        */
#define PKEYSIZE (1+2*PNMSIZ)      /* menu page key size                   */
#define ICOPSIZE (GCMAXPTH+1+FNEXSZ)/* icon path with ; and suggestion fnam*/
#define MDATSZ (TITLSZ+MAXSEL*(PNMSIZ+SHDSIZ+LNDSIZ+FNSIZE))
#define MAINMAPP "WGSMAINM"

static
struct agent mmuagt={              /* agent information structure          */
     MAINMAPP,                     /*   appid                              */
     mmuread,                      /*   read-dynapak function pointer      */
     mmuwrite,                     /*   write-dynapak function pointer     */
     mmuxdone,                     /*   file xfer-done function pointer    */
     mmuabort                      /*   abort-request function pointer     */
};

struct cshdr {                     /* three page types after common data   */
     SHORT pagetype;               /*   page type 0=menu, 1=mod, 2,3=file  */
     CHAR pagnam[PNMSIZ-1];        /*   page name                          */
     GBOOL access;                 /*   has access to the page             */
     GBOOL cango;                  /*   has access to FIND/GO the page     */
};

struct flddef cshdrFDA[]={
     {CVTFLD_SHORT ,1       ,fldoff(cshdr,pagetype),NULL},
     {CVTFLD_CHAR  ,PNMSIZ-1,fldoff(cshdr,pagnam)  ,NULL},
     {CVTFLD_SHORT ,2       ,fldoff(cshdr,access)  ,NULL},
     {CVTFLD_END   ,0       ,0                     ,NULL}
};

struct csmenu {                    /* menu page                            */
     struct cshdr hdr;             /*   common header to pages             */
     SHORT mnutyp;                 /*   menu type (see types below)        */
     GBOOL autosel;                /*   autoselect menu                    */
     LONG ichange;                 /*   time code of changes to icons      */
     SHORT nopts;                  /*   number of selections on the menu   */
     CHAR data[MDATSZ];            /*   menu data                          */
};

struct flddef csmenuFDA[] = {
     {CVTFLD_STRUCT,1,fldoff(csmenu,hdr)    ,cshdrFDA},
     {CVTFLD_SHORT ,2,fldoff(csmenu,mnutyp) ,NULL    },
     {CVTFLD_LONG  ,1,fldoff(csmenu,ichange),NULL    },
     {CVTFLD_SHORT ,1,fldoff(csmenu,nopts)  ,NULL    },
     {CVTFLD_CHAR  ,0,fldoff(csmenu,data)   ,NULL    },
     {CVTFLD_END   ,0,0                     ,NULL    }
};

                                   /* menu type codes                      */
#define ICOMNU -1                  /*   icon menu                          */
#define LBXMNU 0                   /*   listbox menu                       */
#define HPSMNU 1                   /*   hypermedia menu                    */

#define csmenupg ((struct csmenu *)rsptmp)

struct csmod {                     /* module page                          */
     struct cshdr hdr;             /*   common header to pages             */
     SHORT modutype;               /*   mod type: 0=C/S 1=A/A              */
     CHAR appid[AIDSIZ-1];         /*   appid of executable                */
     CHAR cmdstg[CMDSIZ-1];        /*   command stg for entry to module    */
};

struct flddef csmodFDA[] = {
     {CVTFLD_STRUCT,1                ,fldoff(csmod,hdr)     ,cshdrFDA},
     {CVTFLD_SHORT ,1                ,fldoff(csmod,modutype),NULL    },
     {CVTFLD_CHAR  ,AIDSIZ-1+CMDSIZ-1,fldoff(csmod,appid)   ,NULL    },
     {CVTFLD_END   ,0                ,0                     ,NULL    }
};

#define csmodpg ((struct csmod *)rsptmp)

struct csfile {                    /* file page                            */
     struct cshdr hdr;             /*   common header to pages             */
     CHAR filname[FNEXSZ-1];       /*   file name (w/ ext.) being shown    */
     CHAR caption[LNDSIZ-1];       /*   window caption to put on display   */
};

struct flddef csfileFDA[] = {
     {CVTFLD_STRUCT,1                ,fldoff(csfile,hdr)    ,cshdrFDA },
     {CVTFLD_CHAR  ,FNEXSZ-1         ,fldoff(csfile,filname),NULL     },
     {CVTFLD_RTEXT ,LNDSIZ-1         ,fldoff(csfile,caption),NULL     },
     {CVTFLD_END   ,0                ,0                     ,NULL     }
};

#define csfilepg ((struct csfile *)rsptmp)

static
struct startup {                   /* singly linked list of startup apps   */
     CHAR *appid;                  /*   App-ID + optional dll and function */
     GBOOL (*stfunc)(VOID);        /*   permission function to call        */
     struct startup *next;         /*   next startup app                   */
} *cssfirst=NULL;

struct options {                   /* main menu options                    */
     GBOOL invis;                  /*   user is invisible (unsec.)         */
     GBOOL pageon;                 /*   user's page is on/off              */
     GBOOL pageok;                 /*   user is /P OK                      */
     GBOOL caninv;                 /*   user can toggle invis (unsec.)     */
     GBOOL canlook;                /*   user can lookup users (unsec.)     */
     SHORT pagint;                 /*   page interval in minutes           */
     GBOOL deftbstt;               /*   toolbar appears by default         */
     GBOOL usrtbtog;               /*   user able to show/hide toolbar     */
     SHORT confses;                /*   confirm shutdown:1=yes -1=no 0=user*/
     GBOOL confusr;                /*   confirm by default (confses=0)     */
     GBOOL editmenu;               /*   user can run Menu Editor App       */
     SHORT gofind;                 /*   go/find 0=none -1=normal 1=fast    */
     GBOOL overmm;                 /*   set main menu to max size          */
     GBOOL anndaily;               /*   see announcements once daily       */
};

struct flddef optionsFDA[] = {
     {CVTFLD_SHORT,14,fldoff(options,invis),NULL},
     {CVTFLD_END  ,0 ,0                    ,NULL}
};

struct anrqi {                     /* announcement per-request information */
     SHORT reqtype;                /*   type of request                    */
     SHORT index;                  /*   request index for announce()       */
};

/* NOTE: the size of this structure MUST match the #define ANNOSIZE        */

#define anptr ((struct anrqi *)mrqptr)

#define MMRQNULL 0                 /* ordinary request                     */
#define MMRQANNO 1                 /* announcement request                 */
#define MMRQMENU 2                 /* downloading main menu                */

static
struct appref {                    /* cross-reference app-id to module name*/
     CHAR appid[AIDSIZ];           /*   appid                              */
     CHAR modnam[MNMSIZ];          /*   module name                        */
} *apps;                           /*   app elements loaded                */

static
INT numapps;                       /* number of elements in **dlls         */

#define xsize(a) (*((USHORT *)a))

struct xstg {                      /* structure for bytestring-encoded stg */
     SHORT stglen;                 /*   length of string                   */
     CHAR stg[1];                  /*   string                             */
};

#define PAGEHEADERSZ  7
struct pheader {                   /* struct: page header in .HPS files    */
     SHORT len;                    /*   length of page header              */
     SHORT version;                /*   version of .HPS file               */
     struct xstg bgfile;           /*   graphic file bytestring            */
};

struct oheader {                   /* struct: object headers in .HPS files */
     SHORT len;                    /*   length of object                   */
     SHORT otype;                  /*   type of object                     */
     struct xstg keyreq;           /*   key required bytestring            */
};

IMPORT_VARIABLE(DFAFILE*) mnubb;   /* menu information btrieve file pointer*/

static
struct mnupag mnutmp,mnutmp2;      /* temporary menu pages for access check*/

static
CHAR *wgsicon,                     /* actual bbs icon file name, w/o ext.  */
     *aboutmsg,                    /* message, if any, for About... box    */
     *eggappid,                    /* appid for easter egg                 */
     *longdesc,                    /* long description set by hasaxs()     */
     *shrtdesc,                    /* short description set by hasaxs()    */
     *hpspp,                       /* path prefix for .HPS files           */
     **hpsfils,                    /* list of all .HPS files in hpspp      */
     **curflist;                   /* per-user current HPS page file lists */

static
INT numhps;                        /* total number of .HPS files in hpsfils*/

static
SHORT confses;                     /* reference confses in struct options  */

static
GBOOL deftbstt,                    /* system default toolbar state         */
      usrtbtog,                    /* users allowed to toggle own toolbars */
      confusr=FALSE;               /* reference confusr in struct options  */

static
struct saunam *rspdpk;             /* modifiable copy of req. dynapak hdr. */

static
CHAR *splashf,                     /* name of graphic file for "splash"    */
     *butext,                      /* text on "splash" screen's button     */
     *splashs;                     /* name of sound file for "splash"      */

static
INT tbbopt,                        /* timer, button or both "splash" opt   */
    artprg;                        /* display art download progress boxes? */

static
INT mnucfg[2];                     /* menu configuration options           */

static
INT spload;                        /* load splash scn in bkgd              */

static
GBOOL *canpage;                    /* nterms array of page-able C/S users  */

#define TIMER   1                  /* "timer" option for splash screen     */
#define BUTTON  2                  /* "button" option for splash screen    */
#define BOTH    3                  /* "both" option for splash screen      */

static
INT tmrval;                        /* value of splash timer (in seconds)   */

SHORT gofind;                      /* option for main menu app go/find     */

static
GBOOL fastgolf;                    /* include launchable files-fast go/find*/

static
HMCVFILE bbmsg;

struct pgllist *aaftop=NULL;       /* first top-of-tree page for a/a       */
struct pgllist *csftop=NULL;       /* first top-of-tree page for c/s       */
struct pgllist *csfgop=NULL;       /* first goable page for c/s            */

static
struct pgllist **curpgl=NULL;      /* current first page                   */

CHAR *medkey;                      /* key required to use menu editor      */
                                   /*   NULL if menu editor not existent   */

static
CHAR *overmmkey=NULL;              /* key required to NOT have main menu   */
                                   /*   maximized, NULL = everyone         */

static
GBOOL anndaily;                    /* client to see announcements once/day */

static
VOID *pageCmdInfo;                 // page cmd info buffer


VOID EXPORT
init__wgsmainm(VOID)               /* agent initialization routine         */
{
     CHAR *sptr;

     register_agent(&mmuagt);
     bbmsg=opnmsg("wgsmainm.mcv");
     dclmrq(sizeof(struct anrqi));
     medkey=isfile("wgsmed.mdf") ? stgopt(MEDKEY) : NULL;
     wgsicon=getmsg(ABOUTICO);
     if ((sptr=strchr(wgsicon,'.')) != NULL) {
          *sptr='\0';
     }
     if (*wgsicon == '\0') {
          wgsicon=IMGICO;
     }
     else {
          wgsicon=strcpy(alcmem(strlen(wgsicon)+1),wgsicon);
     }
     eggappid=stgopt(EGGIE);
     stp4cs(sptr=getmsg(ABOUTLIN));
     while (sptr[0] == '\n') {
          sptr++;
     }
     while (strlen(sptr) > 0 && sptr[strlen(sptr)-1] == '\n') {
          sptr[strlen(sptr)-1]='\0';
     }
     strcpy(aboutmsg=alcmem(strlen(sptr)+1),sptr);
     hpspp=pthopt(HPSPP);
     deftbstt=ynopt(TBDEFT);
     usrtbtog=ynopt(TBALOW);
     switch (tokopt(CONFSES,"ALWAYS","NEVER","CHOICE",NULL)) {
     case 1: /* "ALWAYS" */
          confses=1;
          break;
     case 3: /* "CHOICE" */
          confses=0;
          confusr=ynopt(CONFUSR);
          break;
     default:
     case 2: /* "NEVER" */
          confses=-1;
     }
     splashf=stgopt(SPLASHF);
     butext=stgopt(BUTEXT);
     splashs=stgopt(SPLASHS);
     tbbopt=tokopt(TBBOPT,"TIMER","BUTTON","BOTH");
     tmrval=numopt(TMRVAL,1,60);
     artprg=ynopt(ARTPRG);
     mnucfg[0]=tokopt(ICOLFORE,"BLACK","WHITE","YELLOW","RED","CYAN");
     mnucfg[1]=tokopt(ICOLBACK,"TRANSP","BLACK","WHITE","D-GREY","L-GREY");
     spload=ynopt(SPLOAD);
     switch (tokopt(GOFIND,"NONE","NORMAL","FAST")) {
     case 1: /* "NONE" */
          gofind=0;
          break;
     case 2: /* "NORMAL" */
          gofind=-1;
          break;
     case 3: /* "FAST" */
          gofind=1;
          break;
     default:
          catastro("BAD OPTION:%s FOR GOFIND IN WGSMAINM.MSG",
                   lastwd(stgopt(GOFIND)));
     }
     fastgolf=(gofind == 1) ? ynopt(FASTGOLF) : FALSE;
     if (ynopt(DOMMAX)) {
          overmmkey=stgopt(OVERMM);
     }
     anndaily=ynopt(ANNDAILY);
     setmem(&mnutmp,sizeof(struct mnupag),0);
     setmem(&mnutmp2,sizeof(struct mnupag),0);
     rspdpk=(struct saunam *)alcmem(sizeof(struct saunam));
     gappnams();
     loadhpsl();
     curflist=(CHAR **)alczer(nterms*sizeof(CHAR *));
     canpage=(GBOOL *)alczer(nterms*sizeof(GBOOL));
     cspagerou=pagcsusr;
     hook_connect(pagecon);
     rtkick(1,startups);
     reg_dpk();
     BuildKeys();
     pageCmdInfo=(VOID *)alczer(512);
}

VOID EXPORT
initwc__wgsmainm(VOID)             // Webcast initialization routine       */
{
     init__wgsmainm();
}

static VOID
mmuread(                           /* read-dynapak handler                 */
INT direction,                     /*   read direction: 0=eq, 1=gt, -1=lt  */
struct saunam *dpknam)             /*   dynapak name to read               */
{
     CHAR *dpkstg;
     CHAR icopath[ICOPSIZE];
     struct ffblk fb;
     struct options opts;
     INT i;

     *rspdpk=*dpknam;
     dpkstg=cnvs2d(rspdpk);
     anptr->reqtype=MMRQNULL;
     if (samepato("sa:appinfo ",dpkstg) || samepato("saf:appfile ",dpkstg)) {
          dpkappif(direction);
          return;
     }
     if (samepato("sa:apppath ",dpkstg)) {
          dpkappth();
          return;
     }
     if (samepato("saf:supfile ",dpkstg)) {
          dpksupfl(direction,FALSE);
          return;
     }
     if (samepato("saf:csupfile ",dpkstg)) {
          dpksupfl(direction,TRUE);
          return;
     }
     if (samepato("sa:ssfinf ",dpkstg)) {
          dpkssfinf(direction);
          return;
     }
     if (direction == 0) {
          if (samepato("saf:ssfile ",dpkstg)) {
               dpkssfile(FALSE);
               return;
          }
          if (samepato("saf:cssfile ",dpkstg)) {
               dpkssfile(TRUE);
               return;
          }
          if (samepato("saf:exefile ",dpkstg)) {
               dpkexefl(FALSE);
               return;
          }
          if (samepato("saf:cexefile ",dpkstg)) {
               dpkexefl(TRUE);
               return;
          }
          if (samepato("sa:supfiles ",dpkstg)) {
               dpksupfs();
               return;
          }
     }
     if (!stdchk("")) {  /* ALL HANDLERS AFTER THIS POINT ARE stdchk()'D */
          rejectreq();
          return;
     }
     if (samepato("sau:announce ",dpkstg)) {
          announce(direction);
          return;
     }
     if (samepato("sau:menu ",dpkstg)) {
          dpkmenu(direction);
          return;
     }
     if (samepato("sa:startup ",dpkstg)) {
          dpkstart(direction);
          return;
     }
     if (samepato("sauf:iconfile ",dpkstg)) {
          dpkiconf(direction);
          return;
     }
     if (samepato("sauf:hpsfile ",dpkstg)) {
          dpkhpsf(direction);
          return;
     }
     if (samepato("sau:csgo ",dpkstg)) {
          dpkgofind(direction);
          return;
     }
     if (direction != 0) {  /* ALL HANDLERS AFTER THIS ARE NONDIRECTIONAL */
          rejectreq();
          return;
     }
     if (sameto("sau:hpsmenu ",dpkstg)) {
          dpkhpsmenu();
          return;
     }
     if (samepat("sa:recents",dpkstg)) {
          dpkrecnt();
          return;
     }
     if (samepat("sa:about",dpkstg)) {
          sprintf(vdatmp,"%s %s",bturno,aboutmsg);
          rsp2read(rspdpk,STGLEN,vdatmp,rtextFDA);
          return;
     }
     if (samepat("sa:about eggie",dpkstg)) {
          rsp2read(rspdpk,STGLEN,eggappid,NULL);
          return;
     }
     if (samepat("saf:sysicon",dpkstg)) {
          sprintf(icopath,"%s%s%s",ICONDIR,wgsicon,ICONEXT);
          if (fndfile(&fb,icopath,0) && fb.ff_fsize < 2048L) {
               strcat(icopath,";");
               strcat(icopath,IMGICO);
               strcat(icopath,ICONEXT);
               rsp2read(rspdpk,STGLEN,icopath,NULL);
          }
          else {
               rejectreq();
          }
          return;
     }
     if (samepat("sau:options",dpkstg)) {
          opts.invis=(usrptr->flags&INVISB) ? VBTRUE : FALSE;
          if (pagesetUid != NULL) {
               switch ((*pagesetUid)(usaptr->userid)) {
               case PAGE_OK:
                    opts.pageon=FALSE;
                    opts.pageok=VBTRUE;
                    break;
               case PAGE_OFF:
                    opts.pageon=FALSE;
                    opts.pageok=FALSE;
                    break;
               case PAGE_ON:
                    opts.pageon=VBTRUE;
                    opts.pageok=FALSE;
               }
          }
          else {
               opts.pageon=opts.pageok=FALSE;
          }
          opts.caninv=haskey(glbkeyi) ? VBTRUE : FALSE;
          opts.canlook=haskey(glbkey) ? VBTRUE : FALSE;
          opts.pagint=gpirouUid != NULL ? (*gpirouUid)(usaptr->userid) : defpgint;
          opts.deftbstt=deftbstt ? VBTRUE : FALSE;
          opts.usrtbtog=usrtbtog ? VBTRUE : FALSE;
          opts.confses=confses;
          opts.confusr=confusr ? VBTRUE : FALSE;
          opts.editmenu=medkey != NULL && haskey(medkey) ? VBTRUE : FALSE;
          opts.gofind=gofind;
          opts.overmm=overmmkey != NULL && !haskey(overmmkey) ? VBTRUE : FALSE;
          opts.anndaily=anndaily ? VBTRUE : FALSE;
          rsp2read(rspdpk,sizeof(struct options),&opts,optionsFDA);
          return;
     }
     if (samepat("sau:pgignlst",dpkstg)) {
          rsptmp[0]='\0';
          pageptr=pageGetInfoNum(usrnum);
          if (pageptr->nIgnore > 0) {
               for (i=0 ; i < pageptr->nIgnore-1 ; i++) {
                    strcat(rsptmp,spr("%s%c",pageptr->ignList[i],TAB));
               }
               strcat(rsptmp,pageptr->ignList[pageptr->nIgnore-1]);
          }
          rsp2read(rspdpk,STGLEN,rsptmp,charsFDA);
          return;
     }
     if (samepat("sa:userson",dpkstg)) {
          dpkusron();
          return;
     }
     if (samepato("saf:fpage ",dpkstg)) {
          dpkfpage();
          return;
     }
     if (samepato("saf:bkgdbmp ",dpkstg)) {
          dpkbkgdf();
          return;
     }
     if (samepat("saf:splashfile",dpkstg)) {
          dpksplaf(splashf);
          return;
     }
     if (samepat("saf:splashwav",dpkstg)) {
          dpksplaf(splashs);
          return;
     }
     if (samepat("sa:artopts",dpkstg)) {
          sprintf(rsptmp,"%d\t%d\t%d\t%s\t%d\t%d\t%d",
                   artprg,tbbopt,tmrval,butext,
                   mnucfg[0],mnucfg[1],spload);
          rsp2read(rspdpk,STGLEN,rsptmp,rtextFDA);
          return;
     }
     if (samepato("saf:launchpage ",dpkstg)) {
          dpklaunchpg();
          return;
     }
     rejectreq();
}

static VOID
dpkmenu(                           /* find menu page to return on request  */
INT direction)                     /* read direction                       */
{
     CHAR pagkey[PKEYSIZE];

     pagkey[0]='C';
     if (sameto("menu ",rspdpk->suffix)) {
          stzcpy(&pagkey[1],&rspdpk->suffix[strlen("menu ")],PNMSIZ);
     }
     else {
          pagkey[1]='\0';
     }
     dfaSetBlk(mnubb);
     switch (direction) {
     case 0:
          if (!dfaAcqEQ(menupg,pagkey,0)) {
               rejectreq();
               return;
          }
          break;
     case 1:
          if (!dfaAcqGT(menupg,pagkey,0) || pagkey[0] != menupg->menutype[0]) {
               rejectreq();
               return;
          }
          break;
     case -1:
          if (!dfaAcqLT(menupg,pagkey,0) || pagkey[0] != menupg->menutype[0]) {
               rejectreq();
               return;
          }
          break;
     default:
          rejectreq();
          return;
     }
     sprintf(rspdpk->suffix,"menu %s",menupg->pagnam);
     sendpage("");
}

static VOID
dpkgofind(                         /* return go/find pages                 */
INT direction)                     /* read direction                       */
{
     CHAR suf[SFXSIZ],pagnam[PNMSIZ],desc[SHDSIZ];
     CHAR *pgptr,*keyptr;
     CHAR typ;
     struct pgllist *pg;

     if (direction != 1) {    /* only supports forward directional */
          rejectreq();
          return;
     }
     if (gofind != 1) {
          rejectreq();
          return;
     }
     stlcpy(suf,firstwd(rspdpk->suffix),SFXSIZ);
     pglset(&csfgop);
     stlcat(suf," ",SFXSIZ);
     pgptr=&suf[strlen(suf)];
     if (SFXSIZ-strlen(suf) < PNMSIZ) {
          rejectreq();
          return;
     }
     stlcpy(pagnam,nextwd(),PNMSIZ);
     rsptmp[0]='\0';
     dfaSetBlk(mnubb);
     while (1) {
          pg=pglopggt(pagnam);
          if (pg == NULL) {
               break;
          }
          stlcpy(pagnam,firstwd(pg->pagedata),PNMSIZ);
          /* must deal with possiblity of no key */
          keyptr=skpwrd(pg->pagedata);
          if (*keyptr == ' ') {
               keyptr++;
          }
          if (*keyptr != ' ') {
               if (haskey(firstwd(keyptr))) {
                    continue;
               }
               keyptr=skpwht(skpwrd(keyptr));
          }
          else {
               keyptr=skpwht(keyptr);
          }
          stlcpy(desc,keyptr,SHDSIZ);
          if (1+strlen(pagnam)+1+strlen(desc)+2+strlen(rsptmp) >= MAXDPKV) {
               break;
          }
          stlcpy(pgptr,pagnam,PNMSIZ);
          if ((menupg->flags&FILPAG) && (menupg->flags&NOFPHD)) {
               typ='3';
          }
          else if (menupg->flags&FILPAG) {
               typ='2';
          }
          else if (menupg->flags&MDLPAG) {
               typ='1';
          }
          else {
               typ='0';
          }
          sprintf(&rsptmp[strlen(rsptmp)],"%c%s %s\t",typ,pagnam,desc);
     }
     if (rsptmp[0] == '\0') {
          rejectreq();
     }
     else {
          strcpy(rspdpk->suffix,suf);
          rsp2read(rspdpk,STGLEN,rsptmp,NULL);
     }
}

static VOID
dpkstart(                          /* tell what appid's are startup apps   */
INT direction)                     /* read direction                       */
{
     CHAR *s;
     INT idx,cloop;
     struct startup *cur,*locfirst,*lcur;

     s=skpwht(skpwrd(rspdpk->suffix));
     idx=atoi(s);
     if ((s=strrchr(rspdpk->suffix,' ')) == NULL) {
          rejectreq();
          return;
     }
     locfirst=lcur=NULL;
     for (cur=cssfirst ; cur != NULL ; cur=cur->next) {
          if (cur->stfunc == NULL || (*cur->stfunc)()) {
               if (locfirst == NULL) {
                    lcur=locfirst=
                         (struct startup *)alcmem(sizeof(struct startup));
               }
               else {
                    lcur=locfirst->next=
                         (struct startup *)alcmem(sizeof(struct startup));
               }
               *lcur=*cur;
               lcur->next=NULL;
          }
     }
     s++;
     if (*s == '\0' && direction == 1) {
          idx=-1;
     }
     while (1) {
          idx+=direction;
          if (idx < 0) {
               rejectreq();
               return;
          }
          for (cur=cssfirst,cloop=0 ; cur != NULL ; cur=cur->next,cloop++) {
               if (cloop == idx) {
                    if (cur->stfunc == NULL || (*cur->stfunc)()) {
                         sprintf(s,"%02d",idx);
                         rsp2read(rspdpk,STGLEN,cur->appid,NULL);
                         while (locfirst != NULL) {
                              lcur=locfirst->next;
                              free(locfirst);
                              locfirst=lcur;
                         }
                         return;
                    }
                    break;
               }
          }
          if (cur == NULL || direction == 0) {
               rejectreq();
               while (locfirst != NULL) {
                    lcur=locfirst->next;
                    free(locfirst);
                    locfirst=lcur;
               }
               return;
          }
     }
}

static VOID
dpkiconf(                          /* transfer icon file                   */
INT direction)                     /* read direction                       */
{
     struct ffblk fb;
     CHAR pagkey[PKEYSIZE];
     CHAR icopath[ICOPSIZE];
     CHAR *s,*icofile;
     INT idx,i,pg=0;

     pagkey[0]='C';
     stzcpy(&pagkey[1],&rspdpk->suffix[strlen("iconfile ")],2*PNMSIZ);
     if ((s=strchr(pagkey,' ')) == NULL) {
          rejectreq();
          return;
     }
     *s++='\0';
     if (*s == '\0') {
          idx=0;
     }
     else {
          idx=atoi(s)+direction;
     }
     if (idx < 0 || idx >= MAXSEL) {
          rejectreq();
          return;
     }
     if ((s=strrchr(rspdpk->suffix,' ')) == NULL) {
          rejectreq();
          return;
     }
     sprintf(++s,"%02d",idx);
     dfaSetBlk(mnubb);
     if ((!sameas(mnutmp2.menutype,pagkey) && !dfaAcqEQ(&mnutmp2,pagkey,0))
        || (mnutmp2.flags&(MDLPAG|FILPAG))
        || !(mnutmp2.flags&DFTDSP)
        || !hasaxs(&mnutmp2)) {
          rejectreq();
          return;
     }
     do {
          s=autosel(&mnutmp2);
     } while (*s != '\0');
     for (i=0 ; i < mnutmp2.npages ; i++) {
          if (haskey(mnutmp2.page[i].keyreq)
            || mnutmp2.page[i].optdsp != 0) {
               if (pg == idx) {
                    break;
               }
               else {
                    pg++;
               }
          }
     }
     if (i >= mnutmp2.npages) {
          rejectreq();
          return;
     }
     icofile=mnutmp2.page[i].iconame;
     if (icofile[0] == '\0') {
          icofile=DEFICO;
     }
     sprintf(icopath,"%s%s%s",ICONDIR,icofile,ICONEXT);
     while (!fndfile(&fb,icopath,0) || fb.ff_fsize > 2048L) {
          if (icofile == DEFICO) {
               rejectreq();
               return;
          }
          else {
               icofile=DEFICO;
               sprintf(icopath,"%s%s%s",ICONDIR,icofile,ICONEXT);
          }
     }
     strcat(icopath,";");
     strcat(icopath,icofile);
     strcat(icopath,ICONEXT);
     rsp2read(rspdpk,STGLEN,icopath,NULL);
}

static void
dpkhpsf(                           /* get hypermedia file                  */
int direction)                     /*   read direction                     */
{
     char *filnam,*found;
     int nitems,i;

     if (curflist[usrnum] == NULL || direction == -1) {
          rejectreq();
          return;
     }
     filnam=skpwrd(&rspdpk->suffix[strlen("hpsfile ")]);
     if (*filnam != ' ') {
          strcpy(filnam," ");
     }
     filnam++;
     nitems=itemcnt(curflist[usrnum]);
     for (i=0 ; i < nitems ; i++) {
          found=itemidx(curflist[usrnum],i);
          if (direction == 0) {
               if (sameas(filnam,found)) {
                    found=spr("%s%s",hpspp,found);
                    if (isfile(found)) {
                         rsp2read(rspdpk,STGLEN,found,NULL);
                    }
                    else {
                         rejectreq();
                    }
                    return;
               }
          }
          else {
               if (stricmp(filnam,found) < 0) {
                    strcpy(filnam,found);
                    found=spr("%s%s",hpspp,found);
                    if (isfile(found)) {
                         rsp2read(rspdpk,STGLEN,found,NULL);
                         return;
                    }
               }
          }
     }
     rejectreq();
}

static VOID
dpkfpage(VOID)                      /* transfer file page file             */
{
     INT i;
     GBOOL good=FALSE;
     CHAR fpath[GCMAXPTH];
     CHAR pagkey[PKEYSIZE];
     static struct mnupag tmpag;

     pagkey[0]='C';
     stzcpy(&pagkey[1],&rspdpk->suffix[6],2*PNMSIZ);
     dfaSetBlk(mnubb);
     if (!dfaAcqEQ(&mnutmp2,pagkey,0)) {
          rejectreq();
          return;
     }
     if ((mnutmp2.flags&(AUTPAG|FILPAG)) == AUTPAG) {
          for (i=0 ; i < mnutmp2.npages; i++) {
               stzcpy(&pagkey[1],mnutmp2.page[i].destpage,PNMSIZ);
               if (dfaAcqEQ(&tmpag,pagkey,0) && hasaxs(&tmpag)) {
                    if (tmpag.flags&FILPAG) {
                         movmem(&tmpag,&mnutmp2,sizeof(struct mnupag));
                         good=TRUE;
                         break;
                    }
                    else {
                         rejectreq();
                         return;
                    }
               }
          }
          if (!good) {
               rejectreq();
               return;
          }
     }
     else if (!(mnutmp2.flags&FILPAG) || !hasaxs(&mnutmp2)) {
          rejectreq();
          return;
     }
     stzcpy(fpath,mnutmp2.fname,GCMAXPTH);
     if (!isfile(fpath)) {
          rejectreq();
          shocst("C/S FILE PAGE FILE MISSING","%s %s",mnutmp2.pagnam,fpath);
          return;
     }
     rsp2read(rspdpk,STGLEN,fpath,NULL);
}

static void
dpkexefl(                          /* transfer app executable              */
GBOOL cmp)                         /*   compressed one?                    */
{
     struct saunam local;
     CHAR appid[AIDSIZ];

     if (!sameas(rspdpk->sysid,msysid)) {
          rejectreq();
          return;
     }
     stzcpy(appid,skpwht(skpwrd(rspdpk->suffix)),AIDSIZ);
     if (!stdchk("") && !sameas(appid,supappid)) {
          rejectreq();
          return;
     }
     setmem(&local,sizeof(struct saunam),0);
     stzcpy(local.appid,MAINMAPP,AIDSIZ);
     stzcpy(local.suffix,spr("appinfo %s",appid),SFXSIZ);
     if (readgdpEQ(&local,0,NULL,NULL) == 0) {
          rejectreq();
          return;
     }
     local.flags=FLGFIL;
     stzcpy(local.suffix,spr("appfile %s EXE",appid),SFXSIZ);
     if (readgdpEQ(&local,0,NULL,NULL) == 0) {
          rejectreq();
          return;
     }
     if (cmp) {
          strcpy(rsptmp,compext(GDPBTR->value));
          if (isfile(rsptmp)) {
               rsp2read(rspdpk,STGLEN,rsptmp,NULL);
               return;
          }
     }
     strcpy(rsptmp,GDPBTR->value);
#ifdef UNIX
     strrpl(rsptmp,'\\','/');
#endif
     if (isfile(rsptmp)) {
          rsp2read(rspdpk,STGLEN,rsptmp,NULL);
          return;
     }
     rejectreq();
#ifdef UNIX
     strrpl(rsptmp,'/','\\');
#endif
}

static VOID
dpksupfl(                          /* transfer support file                */
INT direction,                     /*   direction of read                  */
GBOOL cmp)                         /*   compressed version?                */
{
     struct saunam local;
     CHAR *s,appid[AIDSIZ];
     INT idx;

     if (!sameas(rspdpk->sysid,msysid)) {
          rejectreq();
          return;
     }
     if ((s=strrchr(rspdpk->suffix,' ')) != NULL) {
          idx=atoi(++s);
          if (*s == '\0' && direction == 1) {
               idx=0;
          }
          else {
               idx+=direction;
          }
          if (direction != 0) {
               sprintf(s,"%02d",idx);
          }
     }
     else {
          rejectreq();
          return;
     }
     stzcpy(appid,firstwd(skpwht(skpwrd(rspdpk->suffix))),AIDSIZ);
     if (!stdchk("") && !sameas(appid,supappid)) {
          rejectreq();
          return;
     }
     setmem(&local,sizeof(struct saunam),0);
     stzcpy(local.appid,MAINMAPP,AIDSIZ);
     stzcpy(local.suffix,spr("appinfo %s",appid),SFXSIZ);
     if (readgdpEQ(&local,0,NULL,NULL) == 0
      || idx >= getnsupfs(GDPBTR->value)) {
          rejectreq();
          return;
     }
     local.flags=FLGFIL;
     stzcpy(local.suffix,spr("appfile %s %d",appid,idx),SFXSIZ);
     if (readgdpEQ(&local,0,NULL,NULL) == 0) {
          rejectreq();
          return;
     }
     if (cmp) {
          strcpy(rsptmp,compext(GDPBTR->value));
          if (isfile(rsptmp)) {
               rsp2read(rspdpk,STGLEN,rsptmp,NULL);
               return;
          }
     }
     strcpy(rsptmp,GDPBTR->value);
     if (isfile(rsptmp)) {
          rsp2read(rspdpk,STGLEN,rsptmp,NULL);
          return;
     }
     rejectreq();
}

static VOID
dpksupfs(VOID)                     /* send all support file names          */
{
     CHAR appid[AIDSIZ];
     struct saunam local;
     INT idx,nsupfl;

     if (!sameas(rspdpk->sysid,msysid)) {
          rejectreq();
          return;
     }
     setmem(&local,sizeof(struct saunam),0);
     stzcpy(local.appid,MAINMAPP,AIDSIZ);
     stzcpy(appid,&rspdpk->suffix[9],AIDSIZ);
     if (!stdchk("") && !sameas(appid,supappid)) {
          rejectreq();
          return;
     }
     stzcpy(local.suffix,spr("appinfo %s",appid),SFXSIZ);
     if (readgdpEQ(&local,0,NULL,NULL) == 0) {
          rejectreq();
          return;
     }
     nsupfl=getnsupfs(GDPBTR->value);
     local.flags=FLGFIL;
     vdatmp[0]='\0';
     for (idx=0 ; idx < nsupfl ; idx++) {
          stzcpy(local.suffix,spr("appfile %s %d",appid,idx),SFXSIZ);
          if (readgdpEQ(&local,0,NULL,NULL) == 0) {
               rejectreq();
               return;
          }
          strcat(vdatmp,GDPBTR->value);
          strcat(vdatmp," "); /* note: space is required after last element */
     }
     rsp2read(rspdpk,STGLEN,vdatmp,NULL);
}

static VOID
dpkappif(                          /* return information on an app         */
INT direction)                     /*   read direction                     */
{
     CHAR *appid;

     if (!sameas(rspdpk->sysid,msysid)) {
          rejectreq();
          return;
     }
     appid=firstwd(skpwht(skpwrd(rspdpk->suffix)));
     if (sameas(appid,supappid) || sameas(appid,MAINMAPP)) {
          cycleme(dontzap);
          anptr->reqtype=MMRQMENU;
     }
     if (!stdchk("") && !sameas(appid,supappid)) {
          rejectreq();
          return;
     }
     r2rgdp(direction,rspdpk);
}

static VOID
dpkappth(VOID)                     /* send app exe file name and info.     */
{
     CHAR appid[AIDSIZ],path[2*GCMAXPTH];
     struct saunam local;

     if (!sameas(rspdpk->sysid,msysid)) {
          rejectreq();
          return;
     }
     stzcpy(appid,&rspdpk->suffix[8],AIDSIZ);
     if (!stdchk("") && !sameas(appid,supappid)) {
          rejectreq();
          return;
     }
     setmem(&local,sizeof(struct saunam),0);
     stzcpy(local.appid,MAINMAPP,AIDSIZ);
     stzcpy(local.suffix,spr("appinfo %s",appid),SFXSIZ);
     if (readgdpEQ(&local,0,NULL,NULL) == 0) {
          rejectreq();
          return;
     }
     local.flags=FLGFIL;
     stzcpy(local.suffix,spr("appfile %s EXE",appid),SFXSIZ);
     if (readgdpEQ(&local,0,NULL,NULL) == 0) {
          rejectreq();
     }
     else {
          stzcpy(path,GDPBTR->value,2*GCMAXPTH);
          stzcat(path," ",2*GCMAXPTH);
          stzcpy(local.suffix,spr("appinfo %s",appid),SFXSIZ);
          local.flags=0;
          if (readgdpEQ(&local,0,NULL,NULL) == 0) {
               rejectreq();
          }
          else {
               stzcat(path,GDPBTR->value,2*GCMAXPTH);
               rsp2read(rspdpk,STGLEN,path,NULL);
          }
     }
}

static VOID
dpkrecnt(VOID)                     /* info on one recent caller            */
{
     const CHAR *uid,*tim;
     CHAR *buf;
     INT idx;
     struct recalls *rv;

     buf=prfbuf;
     for (idx=0 ; idx < nreccl ; idx++) {
          rv=&recents[idx];
          uid=rv->userid;
          if (uid[0] != '\0') {
               tim=nctime(rv->logon);
               *buf++=atoi(&tim[0])+32;
               *buf++=atoi(&tim[3])+32;
               *buf++=atoi(&tim[6])+32;
               tim=nctime(rv->logoff);
               *buf++=atoi(&tim[0])+32;
               *buf++=atoi(&tim[3])+32;
               *buf++=atoi(&tim[6])+32;
               sprintf(buf,"%s%c",uid,9);
               buf+=strlen(uid)+1;
          }
     }
     if (buf == prfbuf) {
          rejectreq();
     }
     else {
          rsp2read(rspdpk,(INT)(buf-prfbuf),prfbuf,rtextFDA);
     }
}

static VOID
dpkusron(VOID)                     /* return all users online              */
{
     INT idx;
     struct user *uptr;

     clrmlt();
     for (idx=0 ; idx < nterms ; idx++) {
          uptr=usroff(idx);
          if (uptr->usrcls >= SUPLON && !(uptr->flags&INVISB)) {
               prf("%s%s%c",spr("%02X",channel[idx]),uacoff(idx)->userid,9);
          }
     }
     rsp2read(rspdpk,STGLEN,prfbuf,rtextFDA);
}

static VOID
dontzap(VOID)                      /* prevent user from idling offline     */
{
     usrptr->usetmr=0;
     if (!(usrptr->flags&NOINJO)) {
          usrptr->flags|=NOINJO;  /* no pages while receiving system file  */
     }
}

static VOID
sendpage(                          /* send the page found                  */
CHAR *override)                    /* overriding page name for autsel      */
{
     INT i,typ;
     GBOOL axs;
     CHAR *s,*stg,*ldesc,*sdesc;
     struct cshdr *header;

     for (i=menupg->npages ; i < MAXSEL ; i++) {
          setmem(&menupg->page[i],sizeof(struct pglink),0);
     }
     if (menupg->flags&FILPAG) {
          typ=2;
          header=&csfilepg->hdr;
     }
     else if (menupg->flags&MDLPAG) {
          typ=1;
          header=&csmodpg->hdr;
     }
     else {
          if (*(s=autosel(menupg)) != '\0') {
               sendpage(s);
               return;
          }
          typ=0;
          header=&csmenupg->hdr;
     }
     c2bcpy(header->pagnam,override[0] == '\0' ? menupg->pagnam : override,
         PNMSIZ-1);
     longdesc="";
     shrtdesc="";
     axs=override[0] != '\0' || hasaxs(menupg);
     ldesc=longdesc; /* set by hasaxs() */
     sdesc=shrtdesc; /* set by hasaxs() */
     header->cango=((menupg->flags&CNGOTO) && haskey(menupg->golock)
        && override[0] == '\0');
     header->access=(axs ? VBTRUE : FALSE);
     header->cango=(header->cango ? VBTRUE : FALSE);
     header->pagetype=typ;
     switch (typ) {
     case 0: /* menu */
          if (gethps(menupg->pagnam) != NULL) {
               csmenupg->mnutyp=HPSMNU;
          }
          else {
               csmenupg->mnutyp=((menupg->flags&DFTDSP) ? ICOMNU : LBXMNU);
          }
          csmenupg->autosel=(override[0] != '\0' ? VBTRUE : FALSE);
          csmenupg->ichange=menupg->ichange;
          csmenupg->nopts=0;
          setmem(csmenupg->data,MDATSZ,0);
          if (!axs) {
               rsp2read(rspdpk,fldoff(csmenu,data),csmenupg,csmenuFDA);
               break;
          }
          sprintf(csmenupg->data,"%s\t",menupg->mnuttl);
          for (i=0 ; i < MAXSEL ; i++) {
               if ((haskey(menupg->page[i].keyreq)
                       || menupg->page[i].optdsp > 0)
                       && menupg->page[i].selchr != '\0') {
                    strcat(csmenupg->data,menupg->page[i].destpage);
                    strcat(csmenupg->data,"\t");
                    strcat(csmenupg->data,menupg->page[i].shortd);
                    strcat(csmenupg->data,"\t");
                    strcat(csmenupg->data,menupg->page[i].longd);
                    strcat(csmenupg->data,"\t");
                    strcat(csmenupg->data,menupg->page[i].iconame);
                    strcat(csmenupg->data,"\t");
                    csmenupg->nopts++;
               }
          }
          rsp2read(rspdpk,fldoff(csmenu,data)+strlen(csmenupg->data),csmenupg,
                   csmenuFDA);
          break;
     case 1: /* module */
          csmodpg->modutype=((menupg->flags&CSMPAG) ? 0 : 1);
          c2bcpy(csmodpg->appid,axs ? appid(menupg->modnam) : "",AIDSIZ-1);
          c2bcpy(csmodpg->cmdstg,axs ? menupg->cmdstg : "",CMDSIZ-1);
          rsp2read(rspdpk,sizeof(struct csmod),csmodpg,csmodFDA);
          break;
     case 2: /* file */
          if (menupg->flags&NOFPHD) {
               header->pagetype=3; /* launchable! */
          }
          stg=axs ? menupg->fname : "";
          while ((s=strchr(stg,SL)) != NULL) {
               stg=++s;
          }
          c2bcpy(csfilepg->filname,stg,FNEXSZ-1);
          stg=ldesc[0] == '\0' ? sdesc : ldesc;
          c2bcpy(csfilepg->caption,(axs ? stg : ""),LNDSIZ-1);
          rsp2read(rspdpk,sizeof(struct csfile),csfilepg,csfileFDA);
          break;
     default:
          rejectreq();
     }
}

static CHAR *
autosel(                           /* auto-select menu page support        */
struct mnupag *autmnu)
{
     INT i;
     CHAR pagkey[PKEYSIZE];
     static struct mnupag tmpag;
     static CHAR oldnam[PNMSIZ];

     oldnam[0]='\0';
     if (autmnu->flags&AUTPAG) {
          pagkey[0]='C';
          dfaSetBlk(mnubb);
          for (i=0 ; i < autmnu->npages; i++) {
               stzcpy(&pagkey[1],autmnu->page[i].destpage,PNMSIZ);
               if (haskey(autmnu->page[i].keyreq) && pagkey[1] != '\0') {
                    if (dfaAcqEQ(&tmpag,pagkey,0)) {
                         stzcpy(oldnam,autmnu->pagnam,PNMSIZ);
                         movmem(&tmpag,autmnu,sizeof(struct mnupag));
                         return(oldnam);
                    }
               }
          }
     }
     return("");
}

static GBOOL
hasaxs(                            /* user has direct access to page       */
struct mnupag *page)               /* page to test                         */
{
     GBOOL access;
     INT i;

     dfaSetBlk(mnubb);
     if (sameas("TOP",page->pagnam)) {
          access=TRUE;
     }
     else {
          if (page->parpag[0] == '\0'
             || !dfaAcqEQ(&mnutmp,
                          spr("C%s",page->parpag),
                          0)) {
               access=TRUE;
          }
          else {
               access=FALSE;
               for (i=0 ; i < mnutmp.npages ; i++) {
                    if (sameas(mnutmp.page[i].destpage,page->pagnam)) {
                         access=haskey(mnutmp.page[i].keyreq);
                         longdesc=mnutmp.page[i].longd;
                         shrtdesc=mnutmp.page[i].shortd;
                         break;
                    }
               }
          }
     }
     if (!access) {
          access=((page->flags&CNGOTO) && haskey(page->golock));
     }
     return(access);
}

static VOID
startups(VOID)                     /* determine startup apps               */
{
     struct startup **cur;

     cur=&cssfirst;
     if (tfsopn("wgserv.cfg") == 0) {
          catastro("BBSMAINM: Can't find %s!",fnmcse("wgserv.cfg"));
     }
     while (tfsrdl() != TFSDUN) {
          if (tfstate == TFSLIN && tfspfx("CSTART=")) {
               *cur=(struct startup *)alczer(sizeof(struct startup));
               (*cur)->next=NULL;
               (*cur)->appid=alcdup(strupr(firstwd(tfspst)));
               setstfunc(skpwht(skpwrd(tfspst)),&(*cur)->stfunc);
               cur=&(*cur)->next;
          }
     }
}

static VOID
setstfunc(
CHAR *dllnfunc,
GBOOL (**stfunc)(VOID))
{
     CHAR *dll,*func;
#ifdef GCDOS
     HMODULE handle;
#else
     HINSTANCE hInst;
#endif // GCDOS

     *stfunc=NULL;
     if (*dllnfunc == '\0') {
          return;
     }
     dll=alcdup(firstwd(dllnfunc));
     func=alcdup(spr("_%s",nextwd()));
#ifdef GCDOS
     if (DosGetModHandle(dll,&handle) == 0) {
          if (DosGetProcAddr(handle,func,(PPFN)stfunc) != 0) {
               *stfunc=NULL;
               shocst("STARTUP FUNCTION NOT FOUND",
                    "function %s not found in %s DLL",func,dll);
          }
     }
     else {
          shocst("STARTUP FUNCTION NOT LOADED","%s DLL not referencable",dll);
     }
#else
     if ((hInst=LoadLibrary(dll)) != NULL) {
          *stfunc=(GBOOL (*)(VOID))GetProcAddress(hInst,func);
          if (*stfunc == NULL) {
               shocst("STARTUP FUNCTION NOT FOUND",
                    "function %s not found in %s DLL",func,dll);
          }
     }
     else {
          shocst("STARTUP FUNCTION NOT LOADED","%s DLL not referencable",dll);
     }
#endif // GCDOS
     free(func);
     free(dll);
}

static VOID
loadhpsl(VOID)                     /* load up list of all .HPS files       */
{
     CHAR allfils[GCMAXPTH];
     struct ffblk fb;
     INT count;

     stlcpy(allfils,hpspp,GCMAXPTH);
     stlcat(allfils,"*.HPS",GCMAXPTH);
     numhps=0;
     if (fnd1st(&fb,allfils,0)) {
          do {
               numhps++;
          } while (fndnxt(&fb));
     }
     if (numhps > 0) {
          hpsfils=(CHAR **)alcmem(numhps*sizeof(CHAR *));
          count=0;
          fnd1st(&fb,allfils,0);
          do {
               ASSERT(count < numhps);
               fileparts(GCPART_FILE,fb.ff_name,allfils,sizeof(allfils));
               hpsfils[count++]=alcdup(allfils);
          } while (fndnxt(&fb));
          ASSERT(count == numhps);
     }
}

static CHAR *
gethps(                            /* get .HPS file for page (or NULL)     */
CHAR *pagnam)                      /*   page name to get filename for      */
{
     INT i;
     CHAR filnam[GCMAXFNM];

     stzcpy(filnam,pagnam,GCMAXFNM);
     for (i=0 ; i < numhps ; i++) {
          if (sameas(hpsfils[i],filnam)) {
               return(hpsfils[i]);
          }
     }
     return(NULL);
}

static USHORT
filtpage(                          /* prepare .HPS page for transmission   */
CHAR *page,                        /*   the .HPS page                      */
USHORT totlen)                     /*   total length of .HPS page          */
{
     USHORT retval=0;
     SHORT len;

     vdatmp[0]='\0';
     len=xsize(page)+sizeof(USHORT);
     if (len > totlen || len < sizeof(USHORT)) {
          return(0);
     }
     eatpage(page);
     page+=len;
     retval+=len;
     totlen-=len;
     while (totlen >= sizeof(USHORT)) {
          len=xsize(page)+sizeof(USHORT);
          if (len > totlen || len < sizeof(USHORT)) {
               return(0);
          }
          if (eatobj(page)) {
               if (totlen-len != 0) {
                    movmem(page+len,page,totlen-len);
               }
          }
          else {
               page+=len;
               retval+=len;
          }
          totlen-=len;
     }
     makflist(vdatmp);
     return(retval);
}

static VOID
makflist(                          /* make list of files from page source  */
CHAR *fils)                        /*   TAB-delimited list of filenames    */
{
     CHAR **filnam;
     CHAR *sptr;
     INT i,nfils,lenfl;

     if (curflist[usrnum] != NULL) {
          free(curflist[usrnum]);
          curflist[usrnum]=NULL;
     }
     if (fils[0] == '\0') {
          return;
     }
     strupr(fils);
     nfils=itemcnt(fils);
     filnam=(CHAR **)alcmem(nfils*sizeof(CHAR *));
     sptr=fils;
     i=0;
     while (sptr != NULL) {
          filnam[i]=sptr;
          i++;
          if ((sptr=strchr(sptr,'\t')) != NULL) {
               *sptr++='\0';
          }
     }
     ASSERT(i == nfils);
     sortstgs(filnam,nfils);
     lenfl=0;
     for (i=0 ; i < nfils ; i++) {
          if (i < nfils-1 && sameas(filnam[i],filnam[i+1])) {
               filnam[i]=NULL;
          }
          else {
               lenfl+=strlen(filnam[i])+1;
          }
     }
     curflist[usrnum]=alczer(lenfl);
     for (i=0 ; i < nfils ; i++) {
          if (filnam[i] != NULL) {
               if (curflist[usrnum][0] != '\0') {
                    strcat(curflist[usrnum],"\t");
               }
               strcat(curflist[usrnum],filnam[i]);
          }
     }
}

static VOID
eatpage(                           /* build file list for page files       */
CHAR *page)                        /*   preverified header page            */
{
     UINT ltotal,chop;
     CHAR bgfile[GCMAXFNM],sndfile[GCMAXFNM];
     SHORT tmplen,tmpstglen;
     CHAR *tmpstg;

     tmplen=shtval(page);
     tmpstglen=shtval(page+4);
     tmpstg=page+fldoff(pheader,bgfile)+fldoff(xstg,stg);
     ltotal=tmplen+sizeof(USHORT);
     if (ltotal < PAGEHEADERSZ) {
          return;
     }
     chop=fldoff(pheader,bgfile)+fldoff(xstg,stg);
     ltotal-=chop;
     page+=chop;
     if (tmpstglen > ltotal) {
          return;
     }
     stlcpy(bgfile,tmpstg,min(GCMAXFNM,tmpstglen+1));
     ltotal-=tmpstglen;
     page+=tmpstglen;
     if (ltotal < sizeof(USHORT)) {
          return;
     }
     tmpstglen=shtval(page);
     tmpstg=page+fldoff(xstg,stg);
     if (tmpstglen+fldoff(xstg,stg) > ltotal) {
          return;
     }
     stlcpy(sndfile,tmpstg,min(GCMAXFNM,tmpstglen+1));
     if (strlen(bgfile) > 0) {
          if (vdatmp[0] != '\0') {
               stlcat(vdatmp,"\t",vdasiz);
          }
          stlcat(vdatmp,bgfile,min(strlen(vdatmp)+strlen(bgfile)+1,vdasiz));
     }
     if (strlen(sndfile) > 0) {
          if (vdatmp[0] != '\0') {
               stlcat(vdatmp,"\t",vdasiz);
          }
          stlcat(vdatmp,sndfile,min(strlen(vdatmp)+strlen(sndfile)+1,vdasiz));
     }
}

static GBOOL                       /*   TRUE=destroy object for user       */
eatobj(                            /* check user access and build file list*/
CHAR *page)                        /*   preverified object page            */
{
     UINT ltotal,chop;
     CHAR key[KEYSIZ];
     SHORT tmplen,tmpstglen;
     CHAR *tmpstg;

     tmplen=shtval(page);
     tmpstglen=shtval(page+4);
     tmpstg=page+fldoff(oheader,keyreq)+fldoff(xstg,stg);
     ltotal=tmplen+sizeof(USHORT);
     if (ltotal < PAGEHEADERSZ) {
          return(TRUE);
     }
     chop=fldoff(oheader,keyreq)+fldoff(xstg,stg);
     ltotal-=chop;
     page+=chop;
     if (tmpstglen > ltotal) {
          return(TRUE);
     }
     stlcpy(key,tmpstg,min(KEYSIZ,tmpstglen+1));
     if (!haskey(key)) {
          return(TRUE);
     }
     ltotal-=tmpstglen;
     page+=tmpstglen;
     if (ltotal < sizeof(USHORT)) {
          return(FALSE);
     }
     tmpstglen=shtval(page);
     tmpstg=page+fldoff(xstg,stg);
     if (tmpstglen+fldoff(xstg,stg) > ltotal) {
          return(TRUE);
     }
     if (tmpstglen > 0) {
          if (vdatmp[0] != '\0') {
               stlcat(vdatmp,"\t",vdasiz);
          }
          stlcat(vdatmp,tmpstg,min(strlen(vdatmp)+tmpstglen+1,vdasiz));
     }
     return(FALSE);
}

static VOID
dpkhpsmenu(VOID)                   /* respond with HPS menu                */
{
     CHAR pagkey[PKEYSIZE],*s;
     FILE *fp;
     USHORT rtlen;

     pagkey[0]='C';
     stzcpy(&pagkey[1],&rspdpk->suffix[strlen("hpsmenu ")],PNMSIZ);
     dfaSetBlk(mnubb);
     if (!dfaAcqEQ(menupg,pagkey,0)) {
          rejectreq();
          return;
     }
     do {
          s=autosel(menupg);
     } while (*s != '\0');
     if (!hasaxs(menupg)) {
          rejectreq();
          return;
     }
     s=gethps(menupg->pagnam);
     if (s == NULL) {
          rejectreq();
          return;
     }
     if ((fp=fopen(spr("%s%s.HPS",hpspp,s),FOPRB)) == NULL) {
          rejectreq();
          return;
     }
     rtlen=fread(rsptmp,1,MAXDPKV,fp);
     fclose(fp);
     if (rtlen < 1 || rtlen > MAXDPKV) {
          rejectreq();
          return;
     }
     rtlen=filtpage(rsptmp,rtlen);
     rsp2read(NULL,rtlen,rsptmp,NULL);
}

static VOID
mmuwrite(                          /* write-dynapak handler                */
struct saunam *dpknam,             /*   dynapak name to write              */
USHORT length,                     /*   length of dynapak value            */
VOID *value)                       /*   dynapak value to write             */
{
     CHAR *dpkstg;
     CHAR *msg;
     struct options opts;

     if (!stdchk("")) {
          rejectreq();
          return;
     }
     dpkstg=cnvs2d(dpknam);
     anptr->reqtype=MMRQNULL;
     if (samepat("sau:options",dpkstg)) {
          if (length >= sizeof(struct options)) {
               movmem(value,&opts,sizeof(struct options));
               invis(opts.invis);
               pagestat(opts.pageon,opts.pageok);
               if (setPageIntRou != NULL) {
                    (*setPageIntRou)(usaptr->userid,opts.pagint);
               }
               rsp2write(TRUE,0,NULL,NULL);
               return;
          }
     }
     if (samepat("sau:pagign",dpkstg)) {
          if (length > IGNLSIZ) {
               rejectreq();
               return;
          }
          if ((*ignorePageRou)(usaptr->userid,value)) {
               r2wprf(TRUE);
          }
          else {
               r2wprf(FALSE);
          }
          return;
     }
     if (samepat("sau:pagalw",dpkstg)) {
          if ((*allowPageRou)(usaptr->userid,value)) {
               r2wprf(TRUE);
          }
          else {
               r2wprf(FALSE);
          }
          return;
     }
     if (samepat("sa:pagon",dpkstg)) {
          canpage[usrnum]=TRUE;
          rsp2write(TRUE,0,NULL,NULL);
          return;
     }
     if (sameas("sa:page",dpkstg)) {
          if ((msg=strchr(value, '\t')) != NULL) {
               *msg='\0';
               msg++;
               pageuser(value,msg);
          }
          else {
               pageuser(value,"");
          }
          return;
     }
     if (sameas(dpknam->suffix,"page") && sameas(dpknam->sysid,msysid)) {
          pageuser(dpknam->usrid,value);
          return;
     }
     rejectreq();
}

static VOID
mmuxdone(VOID)                     /* file transfer-done handler           */
{
     if (anptr->reqtype == MMRQMENU) {
          usrptr->flags&=~NOINJO;  /* page on, main menu file received */
     }
}

static VOID
mmuabort(VOID)                     /* abort-request handler                */
{
     if (anptr->reqtype == MMRQMENU) {
          usrptr->flags&=~NOINJO;  /* page on, main menu file receive abort */
     }
}

static VOID
invis(                             /* become visible/invisible             */
GBOOL inv)
{
     if (haskey(glbkeyi)) {
          if (inv) {
               usrptr->flags|=INVISB;
               usaptr->flags|=GOINVB;
          }
          else {
               usrptr->flags&=~INVISB;
               usaptr->flags&=~GOINVB;
          }
          if (tlcInvisRou != NULL) {
               (*tlcInvisRou)(usrnum);
          }
     }
}

static VOID
pagestat(                          /* set page on/off/ok                   */
GBOOL ison,                        /* is to be set on?                     */
GBOOL isok)                        /* is to be set ok (priority)?          */
{
     if (spagesetUid != NULL) {
          (*spagesetUid)(usaptr->userid,ison,isok);
     }
}

static VOID
pageuser(                          /* page user with message               */
CHAR *uid,                         /* user to page                         */
CHAR *msg)                         /* message to page                      */
{
     GBOOL okpage;

     if (pagerouUid == NULL) {
          rejectreq();
          return;
     }
     okpage=((*pagerouUid)(usaptr->userid,uid,msg) == 1);
     if (okpage) {
          clrprf();
     }
     r2wprf(okpage);
}

static INT
pagcsusr(                          /* page c/s user with a message         */
CHAR *from,                        /* user the message is from             */
CHAR *msg)                         /* message to send                      */
{
     CHAR *stg;

     setmem(rspdpk,sizeof(struct saunam),0);
     if (!(othusp->flags&NOINJO)) {
          if (canpage[othusn]) {
               if (cnvd2s(spr("su=%s;:pagefrom",othuap->userid),rspdpk)) {
                    if ((stg=malloc(strlen(from)+strlen(msg)+1+1)) == NULL) {
                         shocst("C/S PAGE FAILED - OUT OF MEMORY"
                          , "%s was unable to page %s", from, othuap->userid);
                    }
                    else {
                         sprintf(stg, "%s\t%s", from, msg);
                         senddpk(othusn, MAINMAPP, NORMAL, rspdpk, STGLEN, stg
                          , rtextFDA);
                         free(stg);
                         return(1);
                    }
               }
          }
          else {
               setmbk(bbmsg);
               if (*msg == '\0') {
                    prfmlt(PAGMSG,from);
               }
               else {
                    prfmlt(PAGNOT,from,msg);
               }
               injoth();
               clrmlt();
               rstmbk();
               return(1);
          }
     }
     return(0);
}

static VOID
gappnams(VOID)                     /* get module names vs. appids          */
{
     numapps=0;
     if (tfsopn("wgserv.cfg") == 0) {
          catastro("Can't find APP list (%s)!",fnmcse("wgserv.cfg"));
     }
     while (tfsrdl() != TFSDUN) {
          if (tfstate == TFSLIN && tfspfx("APP=")) {
               numapps++;
          }
     }
     if (numapps == 0) {
          return;
     }
     apps=(struct appref *)alczer(numapps*sizeof(struct appref));
     numapps=0;
     if (tfsopn("wgserv.cfg") == 0) {
          catastro("Can't find APP list (%s)!",fnmcse("wgserv.cfg"));
     }
     while (tfsrdl() != TFSDUN) {
          if (tfstate == TFSLIN && tfspfx("APP=")) {
               stzcpy(apps[numapps].appid,firstwd(tfspst),AIDSIZ);
               stzcpy(apps[numapps].modnam,skpwht(skpwrd(tfspst)),MNMSIZ);
               numapps++;
          }
     }
}

static CHAR *
appid(                             /* given module name, return its appid  */
CHAR *modnam)
{
     INT i;
     struct appref *dr;

     for (i=0 ; i < numapps ; i++) {
          dr=&apps[i];
          if (sameas(dr->modnam,modnam)) {
               return(dr->appid);
          }
     }
     return("");
}

static VOID
announce(                          /* respond with announcements           */
INT direction)                     /*   index direction                    */
{
     CHAR *s;

     setmem(mrqptr,mrqsiz,0);
     anptr->reqtype=MMRQANNO;
     s=skpwht(skpwrd(rspdpk->suffix));
     if (*s == '\0') {
          if (direction > 0) {
               anptr->index=0;
          }
          else {
               rejectreq();
               return;
          }
     }
     else {
          anptr->index=atoi(s)+direction;
     }
     if (!annvec()) {
          cycleme(annvecyc);
     }
}

static GBOOL                       /*   returns TRUE is request answered   */
annvec(VOID)                       /* call hook_announce vectors           */
{
     struct saunam sn;
     GBOOL rv=FALSE;

     annomem=mrqptr+ANNOSIZE;
     if (anptr->index <= ninarr(annohdl)) {
          *rsptmp='\0';
          if (anptr->index == 0) {
               addanno(sv.lonmsg);
          }
          else {
               clrprf();
               (*(VOID (**)(VOID))arrelem(annohdl,anptr->index-1))();
          }
          if (itemcnt(rsptmp) > 1) {
               if (strlen(itemidx(rsptmp,0)) > 0
                 && cnvd2s(spr("sau:announce %04d",anptr->index),&sn)) {
                    rsp2read(&sn,STGLEN,rsptmp,rtextFDA);
                    rv=TRUE;
               }
               else {
                    anptr->index++;
                    setmem(annomem,mrqsiz-ANNOSIZE,0);
               }
          }
     }
     else {
          rejectreq();
          rv=TRUE;
     }
     return(rv);
}

static VOID
annvecyc(VOID)                     /* cycleme() call annvec()              */
{
     annvec();
}

static VOID
dpkbkgdf(VOID)
{
     CHAR fname[GCMAXPTH];

     stzcpy(fname,skpwht(skpwrd(rspdpk->suffix)),FNSIZE);
     if (strstr(fname,"..") != NULL) {
          rejectreq();
          return;
     }
     sprintf(rsptmp,"%s%s.BMP",hpspp,fname);
     if (!isfile(rsptmp)) {
          sprintf(rsptmp,"%s%s.WMF",hpspp,fname);
          if (!isfile(rsptmp)) {
               sprintf(rsptmp,"%s%s.DIB",hpspp,fname);
               if (!isfile(rsptmp)) {
                    rejectreq();
                    return;
               }
          }
     }
     rsp2read(rspdpk,STGLEN,rsptmp,NULL);
}

static VOID
dpksplaf(                          /* return splash (graphic/snd) file     */
CHAR *filename)                    /*   name of file to return             */
{
     sprintf(rsptmp,"%s%s",hpspp,filename);
     if (filename[0] != '\0' && isfile(rsptmp)) {
          rsp2read(rspdpk,STGLEN,rsptmp,NULL);
     }
     else {
          rejectreq();
     }
}

static VOID
dpklaunchpg(VOID)                  /* launch a file page - request         */
{
     CHAR pagkey[PKEYSIZE];

     pagkey[0]='C';
     stzcpy(&pagkey[1],&rspdpk->suffix[strlen("launchpage ")],PNMSIZ);
     dfaSetBlk(mnubb);
     if (dfaAcqEQ(menupg,pagkey,0)) {
          if (menupg->flags&FILPAG) {
               if (menupg->flags&NOFPHD) {
                    if (hasaxs(menupg)) {
                         if (isfile(menupg->fname)) {
                              rsp2read(rspdpk,STGLEN,menupg->fname,NULL);
                              return;
                         }
                    }
               }
          }
     }
     rejectreq();
}

static VOID
dpkssfile(                         /* read shared support file             */
GBOOL cmp)                         /*   compressed version?                */
{
     struct dpkwv *inf;
     CHAR sfpath[GCMAXPTH],tmpname[GCMAXPTH];

     if (!sameas(rspdpk->sysid,msysid)
        || (inf=getssfinf(skpwht(skpwrd(rspdpk->suffix)))) == NULL) {
          rejectreq();
          return;
     }
     fileparts(GCPART_FNAM,inf->filnam,tmpname,sizeof(tmpname));
     sprintf(sfpath,"%s"SLS"%s",SSFDIR,tmpname);
     if (cmp) {
          strcpy(rsptmp,compext(sfpath));
          if (isfile(rsptmp)) {
               rsp2read(rspdpk,STGLEN,rsptmp,NULL);
               return;
          }
     }
     strcpy(rsptmp,sfpath);
     if (isfile(rsptmp)) {
          rsp2read(rspdpk,STGLEN,rsptmp,NULL);
          return;
     }
     rejectreq();
}

static VOID
dpkssfinf(                         /* return support file name & verinfo   */
INT direction)
{
     CHAR appid[AIDSIZ],*fnum,path[GCMAXPTH];
     INT num;
     struct saunam genam;
     struct dpkwv *inf;

     if (!sameas(rspdpk->sysid,msysid)) {
          rejectreq();
          return;
     }
     stzcpy(appid,firstwd(skpwht(skpwrd(rspdpk->suffix))),AIDSIZ);
     if (!stdchk("") && !sameas(appid,supappid)) {
          rejectreq();
          return;
     }
     fnum=nextwd();
     if (*fnum == '\0' && direction == 1) {
          num=0;
     }
     else {
          num=atoi(fnum)+direction;
     }
     if (num < 0) {
          rejectreq();
          return;
     }
     setmem(&genam,sizeof(struct saunam),0);
     stzcpy(genam.appid,MAINMAPP,AIDSIZ);
     genam.flags=FLGFIL;
     sprintf(genam.suffix,"%s %s %d",firstwd(rspdpk->suffix),appid,num);
     sprintf(rspdpk->suffix,"%s %s %03d",firstwd(genam.suffix),appid,num);
     if (readgdpEQ(&genam,GCMAXPTH,path,NULL) && (inf=getssfinf(path)) != NULL) {
          stlcat(path,"\t",GCMAXPTH);
          stlcat(path,l2as(inf->size),GCMAXPTH);
          stlcat(path,"\t",GCMAXPTH);
          stlcat(path,ncdate(inf->date),GCMAXPTH);
          stlcat(path,"\t",GCMAXPTH);
          stlcat(path,nctime(inf->time),GCMAXPTH);
          stlcat(path,"\t",GCMAXPTH);
          stlcat(path,l2as(inf->msl),GCMAXPTH);
          stlcat(path,"\t",GCMAXPTH);
          stlcat(path,l2as(inf->lsl),GCMAXPTH);
          rsp2read(rspdpk,STGLEN,path,NULL);
     }
     else {
          rejectreq();
     }
}

static INT
getnsupfs(                         /* return number of support files       */
CHAR *data)                        /*   when given an appinfo value        */
{
     return(atoi(itemidxd(data,2,"\n")));
}

static struct dpkwv *
getssfinf(                         /* return shared support file info      */
CHAR *filnam)                      /*   filename to return for             */
{
     struct saunam genam;
     static struct dpkwv bestinf;

     setmem(&genam,sizeof(struct saunam),0);
     stlcpy(genam.appid,MAINMAPP,AIDSIZ);
     genam.flags=FLGFIL;
     sprintf(genam.suffix,"ssfile %s",filnam);
     if (readgdpEQ(&genam,sizeof(struct dpkwv),&bestinf,dpkwvFDA)) {
          return(&bestinf);
     }
     return(NULL);
}

static CHAR *
compext(                           /* return path w/ compressed file name  */
CHAR *filnam)                      /*   [path and] file name, uncompressed */
{
     CHAR *s;
     CHAR tmpname[GCMAXPTH];
     static CHAR retval[GCMAXPTH];

     fileparts(GCPART_PATH,filnam,tmpname,sizeof(tmpname));
     stlcpy(retval,tmpname,GCMAXPTH);
     stlcat(retval,"COMPRESS\\",GCMAXPTH);
     fileparts(GCPART_FNAM,filnam,tmpname,sizeof(tmpname));
     stlcat(retval,tmpname,GCMAXPTH);
     s=strrchr(retval,'.');
     if (s == NULL) {
          stlcat(retval,"._",GCMAXPTH);
     }
     else {
          if (strlen(s) == 4) {
               retval[strlen(retval)-1]='_';
          }
          else {
               stlcat(retval,"_",GCMAXPTH);
          }
     }
     return(retval);
}

static VOID
pagecon(VOID)                      /* hook_connect routine to clear flag   */
{
     canpage[usrnum]=FALSE;
     pageLogon();
}

static VOID
reg_dpk(VOID)
{
     register_dpkfda(MAINMAPP,"sau:options",optionsFDA);
}

static VOID
BuildKeys(VOID)                    /* rebuild top-of-tree & goable keys    */
{
     dfaSetBlk(mnubb);
     if (dfaQueryLO(0)) {
          struct mnupag *Temp;

          Temp=(struct mnupag *)alcmem(sizeof(struct mnupag));
          do {
               CHAR pagnkey[PNMSIZ+KEYSIZ+SHDSIZ],*descptr;
               SHORT flags;
               LONG abspos;

               dfaAbsRec(Temp,0);
               flags=Temp->flags;
               switch (Temp->menutype[0]) {
               case 'A':  // A/A page.
                    if ((flags&TPOTRE) != 0) {
                         pglset(&aaftop);
                         pglopins(Temp->pagnam);
                    }
                    break;
               case 'C':  // C/S Page
                    if ((flags&TPOTRE) != 0) {
                         pglset(&csftop);
                         pglopins(Temp->pagnam);
                    }
                    if (gofind == 1 && (flags&(TPOTRE|CNGOTO)) != 0
                              && (fastgolf || (flags&NOFPHD) == 0)) {
                         pglset(&csfgop);
                         abspos=dfaAbs();
                         descptr=gofdesc(Temp);
                         dfaGetAbs(Temp,abspos,0);
                         ASSERT(strlen(Temp->pagnam) < PNMSIZ);
                         ASSERT(strlen(Temp->golock) < KEYSIZ);
                         ASSERT(strlen(descptr) < SHDSIZ);
                         sprintf(pagnkey,"%s %s %s",Temp->pagnam,
                                                    Temp->golock,
                                                    descptr);
                         pglopins(pagnkey);
                    }
                    break;
               default:   // nuke unknown record type
                    dfaDelete();
                    break;
               }
          } while (dfaQueryNX());
          free(Temp);
     }
     dfaRstBlk();
}

CHAR *                             /*   returns "" for orphan              */
gofdesc(                           /* return description of page from menu */
struct mnupag *mp)                 /*   page to get description of         */
{
     static CHAR retval[SHDSIZ];
     static struct mnupag tmpag;

     retval[0]='\0';
     if (mp->parpag[0] != '\0') {
          dfaSetBlk(mnubb);
          if (dfaAcqEQ(&tmpag,spr("%c%s",mp->menutype[0],mp->parpag),0)) {
               INT i;

               for (i=0 ; i < tmpag.npages ; i++) {
                    if (sameas(tmpag.page[i].destpage,mp->pagnam)) {
                         stlcpy(retval,tmpag.page[i].shortd,SHDSIZ);
                         break;
                    }
               }

          }
          dfaRstBlk();
     }
     return(retval);
}

VOID
pglset(                            /* set current linked list              */
struct pgllist **pgltop)
{
     curpgl=pgltop;
}

VOID
pglclear(VOID)                     /* destroy current linked list          */
{
     struct pgllist *mp;

     while ((mp=*curpgl) != NULL) {
          *curpgl=mp->next;
          free(mp);
     }
}

VOID
pglopins(                          /* insert a page key in memory          */
CHAR *pagedata)                    /*   page name/data                     */
{
     CHAR pnm[PNMSIZ];
     INT cpval;
     struct pgllist *mp,*newl,*prev;

     stzcpy(pnm,firstwd(pagedata),PNMSIZ);
     strupr(pnm);
     newl=(struct pgllist *)alcmem(sizeof(struct pgllist)+strlen(pagedata));
     strcpy(newl->pagedata,pnm);
     strcat(&newl->pagedata[strlen(pnm)],&pagedata[strlen(pnm)]);
     prev=NULL;
     mp=*curpgl;
     while (1) {
          if (mp == NULL || (cpval=strcmp(pnm,firstwd(mp->pagedata))) <= 0) {
               if (prev == NULL) {
                    newl->next=*curpgl;
                    *curpgl=newl;
               }
               else {
                    newl->next=mp;
                    prev->next=newl;
               }
               if (mp != NULL && cpval == 0) {
                    newl->next=mp->next;
                    free(mp);
               }
               break;
          }
          prev=mp;
          mp=mp->next;
     }
}

GBOOL                              /*   TRUE=succeeded, FALSE=no such page */
pglopdel(                          /* delete a page key from memory        */
CHAR *pagnam)                      /*   page name                          */
{
     CHAR pnm[PNMSIZ];
     INT cpval;
     struct pgllist *mp,*prev;

     stzcpy(pnm,firstwd(pagnam),PNMSIZ);
     strupr(pnm);
     prev=NULL;
     mp=*curpgl;
     while (mp != NULL && ((cpval=strcmp(pnm,firstwd(mp->pagedata))) >= 0)) {
          if (cpval == 0) {
               if (prev == NULL) {
                    *curpgl=mp->next;
               }
               else {
                    prev->next=mp->next;
               }
               free(mp);
               return(TRUE);
          }
          prev=mp;
          mp=mp->next;
     }
     return(FALSE);
}

struct pgllist *                   /*   page, or NULL if no exist          */
pglopgeq(                          /* get page struct in memory            */
CHAR *pagnam)                      /*   page name                          */
{
     CHAR pnm[PNMSIZ];
     INT cpval;
     struct pgllist *mp;

     stzcpy(pnm,firstwd(pagnam),PNMSIZ);
     strupr(pnm);
     mp=*curpgl;
     while (mp != NULL && ((cpval=strcmp(pnm,firstwd(mp->pagedata))) >= 0)) {
          if (cpval == 0) {
               return(mp);
          }
          mp=mp->next;
     }
     return(NULL);
}

struct pgllist *                   /*   page, or NULL if no exist          */
pglopggt(                          /* get next greater page struct in mem  */
CHAR *pagnam)                      /*   page name                          */
{
     CHAR pnm[PNMSIZ];
     INT cpval;
     struct pgllist *mp;

     stzcpy(pnm,firstwd(pagnam),PNMSIZ);
     strupr(pnm);
     mp=*curpgl;
     while (mp != NULL) {
          cpval=strcmp(pnm,firstwd(mp->pagedata));
          if (cpval < 0) {
               return(mp);
          }
          mp=mp->next;
     }
     return(NULL);
}

struct pgllist *                   /*   page, or NULL if no exist          */
pglopglt(                          /* get next lesser page struct in mem   */
CHAR *pagnam)                      /*   page name                          */
{
     CHAR pnm[PNMSIZ];
     INT cpval;
     struct pgllist *mp,*prev;

     stzcpy(pnm,firstwd(pagnam),PNMSIZ);
     strupr(pnm);
     mp=*curpgl;
     prev=NULL;
     while (mp != NULL) {
          cpval=strcmp(pnm,firstwd(mp->pagedata));
          if (cpval >= 0) {
               return(prev);
          }
          prev=mp;
          mp=mp->next;
     }
     return(NULL);
}

