/***************************************************************************
 *                                                                         *
 *   AAFOR.C                                                               *
 *                                                                         *
 *   Copyright (c) 1988-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   This is the Worldgroup ASCII/ANSI Forums (public messaging areas)     *
 *   handler.  (was SIGS.C)                                                *
 *                                                                         *
 *                                                - J. Alvrus  8/2/94      *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "fsdbbs.h"
#include "gme.h"
#include "galmsg.h"
#include "emlfor.h"
#include "aaef.h"

#define FILREV "$Revision: 17 $"

#define ACLVSZ      9              /* size of FSD access level string      */
#define CHGSIZ      7              /* size of FSD charge string            */
#define PFNSIZ      9              /* size of FSD profanity level string   */

#define CRTMPSZ     (FORNSZ+TPCSIZ+UIDSIZ+(2*GCMAXPTH)+KEYSIZ+3*ACLVSZ   \
                    +8*CHGSIZ+PFNSIZ)/* FSD answer buffer size for creating*/
#define CNAME       0              /* FSD - Create forum field indexes     */
#define CTOPIC      1
#define CFOROP      2
#define CDATFIL     3
#define CATTPATH    4
#define CFORLOK     5
#define CDFNPV      6
#define CDFPRV      7
#define CMXNPV      8
#define CCHGMSG     9
#define CCHGRDM     10
#define CCHGATT     11
#define CCHGADL     12
#define CCHGUPK     13
#define CCHGDPK     14
#define CCCR        15
#define CMSGLIF     16
#define CPFNLVL     17

#define EDTMPSZ     (FORNSZ+TPCSIZ+UIDSIZ+(2*GCMAXPTH)+KEYSIZ+3*ACLVSZ\
                    +8*CHGSIZ+PFNSIZ) /* FSD answer buffer size for editing*/
#define ENAME       0              /* FSD - Edit forum field indexes       */
#define EDATE       1
#define ETOPIC      2
#define EFOROP      3
#define EDATFIL     4
#define EATTPATH    5
#define EFORLOK     6
#define EDFNPV      7
#define EDFPRV      8
#define EMXNPV      9
#define ECHGMSG     10
#define ECHGRDM     11
#define ECHGATT     12
#define ECHGADL     13
#define ECHGUPK     14
#define ECHGDPK     15
#define ECCR        16
#define EMSGLIF     17
#define EPFNLVL     18

GBOOL forinp(VOID);
VOID forsts(VOID);
VOID forhup(VOID);

struct module formib={             /* Forums module interface block        */
     "",                           /*   module name (from .MDF file)       */
     NULL,                         /*   logon routine                      */
     forinp,                       /*   input routine                      */
     forsts,                       /*   status handler                     */
     NULL,                         /*   injoth() handler                   */
     NULL,                         /*   logoff routine                     */
     forhup,                       /*   hangup routine                     */
     NULL,                         /*   midnight clean up                  */
     NULL,                         /*   delete account                     */
     NULL                          /*   shutdown routine                   */
};

INT forstate;                      /* state code for Forums module         */

GBOOL fortlc;                      /* allow normal users to teleconference */

CHAR crtfmt[]=                     /* Create forum answer string format    */
     "CNAME=%s%c"
     "CTOPIC=%s%c"
     "CFOROP=%s%c"
     "CDATFIL=%s%c"
     "CATTPATH=%s%c"
     "CFORLOK=%s%c"
     "CDFNPV=%s%c"
     "CDFPRV=%s%c"
     "CMXNPV=%s%c"
     "CCHGMSG=%s%c"
     "CCHGRDM=%s%c"
     "CCHGATT=%s%c"
     "CCHGADL=%s%c"
     "CCHGUPK=%s%c"
     "CCHGDPK=%s%c"
     "CCCR=%s%c"
     "CMSGLIF=%s%c"
     "CPFNLVL=%s%c";

CHAR edtfmt[]=                     /* Edit forum answer string format      */
     "ENAME=%s%c"
     "EDATE=%s%c"
     "ETOPIC=%s%c"
     "EFOROP=%s%c"
     "EDATFIL=%s%c"
     "EATTPATH=%s%c"
     "EFORLOK=%s%c"
     "EDFNPV=%s%c"
     "EDFPRV=%s%c"
     "EMXNPV=%s%c"
     "ECHGMSG=%s%c"
     "ECHGRDM=%s%c"
     "ECHGATT=%s%c"
     "ECHGADL=%s%c"
     "ECHGUPK=%s%c"
     "ECHGDPK=%s%c"
     "ECCR=%s%c"
     "EMSGLIF=%s%c"
     "EPFNLVL=%s%c";

CHAR crtfsp[]=                     /* Create forum field specifications    */
     "CNAME(NOSPACES) "
     "CTOPIC "
     "CFOROP "
     "CDATFIL(NOSPACES) "
     "CATTPATH(NOSPACES) "
     "CFORLOK(NOSPACES) "
     "CDFNPV(ALT=ZERO ALT=READ ALT=DOWNLOAD ALT=WRITE ALT=UPLOAD ALT=CO-OP "
             "ALT=FORUM-OP MULTICHOICE) "
     "CDFPRV(ALT=ZERO ALT=READ ALT=DOWNLOAD ALT=WRITE ALT=UPLOAD ALT=CO-OP "
             "ALT=FORUM-OP MULTICHOICE) "
     "CMXNPV(ALT=ZERO ALT=READ ALT=DOWNLOAD ALT=WRITE ALT=UPLOAD ALT=CO-OP "
             "ALT=FORUM-OP MULTICHOICE) "
     "CCHGMSG(MIN=-32767 MAX=32767) "
     "CCHGRDM(MIN=-32767 MAX=32767) "
     "CCHGATT(MIN=-32767 MAX=32767) "
     "CCHGADL(MIN=-32767 MAX=32767) "
     "CCHGUPK(MIN=-32767 MAX=32767) "
     "CCHGDPK(MIN=-32767 MAX=32767) "
     "CCCR(MIN=-32767 MAX=32767) "
     "CMSGLIF(MIN=-1 MAX=32767) "
     "CPFNLVL(ALT=NONE ALT=MILD ALT=MODERATE ALT=SEVERE ALT=DEFAULT MULTICHOICE) "
     "DONE(ALT=SAVE ALT=QUIT ALT=EDIT MULTICHOICE)";

CHAR fedtfsp[]=                    /* Forum-Op edit forum field spec       */
     "ENAME(NOSPACES) "
     "EDATE "
     "ETOPIC "
     "EFOROP "
     "EDATFIL(NOSPACES) "
     "EATTPATH(NOSPACES) "
     "EFORLOK(NOSPACES) "
     "EDFNPV(ALT=ZERO ALT=READ ALT=DOWNLOAD ALT=WRITE ALT=UPLOAD ALT=CO-OP "
             "MULTICHOICE) "
     "EDFPRV(ALT=ZERO ALT=READ ALT=DOWNLOAD ALT=WRITE ALT=UPLOAD ALT=CO-OP "
             "MULTICHOICE) "
     "EMXNPV(ALT=ZERO ALT=READ ALT=DOWNLOAD ALT=WRITE ALT=UPLOAD ALT=CO-OP "
             "MULTICHOICE) "
     "ECHGMSG(MIN=-32767 MAX=32767) "
     "ECHGRDM(MIN=-32767 MAX=32767) "
     "ECHGATT(MIN=-32767 MAX=32767) "
     "ECHGADL(MIN=-32767 MAX=32767) "
     "ECHGUPK(MIN=-32767 MAX=32767) "
     "ECHGDPK(MIN=-32767 MAX=32767) "
     "ECCR(MIN=-32767 MAX=32767) "
     "EMSGLIF(MIN=-1 MAX=32767) "
     "EPFNLVL(ALT=NONE ALT=MILD ALT=MODERATE ALT=SEVERE ALT=DEFAULT MULTICHOICE) "
     "DONE(ALT=SAVE ALT=QUIT ALT=EDIT MULTICHOICE)";

CHAR sedtfsp[]=                    /* Sysop edit forum field spec          */
     "ENAME(NOSPACES) "
     "EDATE "
     "ETOPIC "
     "EFOROP "
     "EDATFIL(NOSPACES) "
     "EATTPATH(NOSPACES) "
     "EFORLOK(NOSPACES) "
     "EDFNPV(ALT=ZERO ALT=READ ALT=DOWNLOAD ALT=WRITE ALT=UPLOAD ALT=CO-OP "
             "ALT=FORUM-OP MULTICHOICE) "
     "EDFPRV(ALT=ZERO ALT=READ ALT=DOWNLOAD ALT=WRITE ALT=UPLOAD ALT=CO-OP "
             "ALT=FORUM-OP MULTICHOICE) "
     "EMXNPV(ALT=ZERO ALT=READ ALT=DOWNLOAD ALT=WRITE ALT=UPLOAD ALT=CO-OP "
             "ALT=FORUM-OP MULTICHOICE) "
     "ECHGMSG(MIN=-32767 MAX=32767) "
     "ECHGRDM(MIN=-32767 MAX=32767) "
     "ECHGATT(MIN=-32767 MAX=32767) "
     "ECHGADL(MIN=-32767 MAX=32767) "
     "ECHGUPK(MIN=-32767 MAX=32767) "
     "ECHGDPK(MIN=-32767 MAX=32767) "
     "ECCR(MIN=-32767 MAX=32767) "
     "EMSGLIF(MIN=-1 MAX=32767) "
     "EPFNLVL(ALT=NONE ALT=MILD ALT=MODERATE ALT=SEVERE ALT=DEFAULT MULTICHOICE) "
     "DONE(ALT=SAVE ALT=QUIT ALT=EDIT MULTICHOICE)";

CHAR *edtfsp;                      /* aliases fedtfsp and sedtfsp          */

VOID entrfor(VOID);
VOID joinfor(USHORT forum);
VOID sfmainm(VOID);
static VOID forerr(INT msg);
static VOID chk4new(SHORT rc);
VOID sstart(GBOOL newflg);
static VOID sgonum(VOID);
static GBOOL startat(GBOOL newflg,INT hlpmsg);
static VOID jumpto(LONG msgid);
static VOID goscan(SHORT rc);
static VOID scan(VOID);
static VOID goscstt(VOID);
static VOID nxtscn(SHORT rc);
static VOID prvscn(SHORT rc);
static VOID nocscn(SHORT rc);
static VOID nocscp(SHORT rc);
VOID sfscan(VOID);
static VOID sthrfbp(VOID);
static VOID thrprnt(SHORT rc);
static VOID entrthr(VOID);
static VOID exithr(VOID);
static VOID readit(VOID);
VOID fmarkrd(SHORT rc);
VOID fmrkdone(SHORT rc);
static VOID chkatt(VOID);
VOID fdndone(SHORT rc);
static VOID gordstt(VOID);
VOID sfread(VOID);
VOID hdlnext(VOID);
VOID hdlprev(VOID);
VOID hdlleav(VOID);
static VOID forrpl(VOID);
static VOID emlrpl(VOID);
VOID frdone(SHORT rc);
VOID sfauth(VOID);
VOID sforop(VOID);
VOID authop(CHAR c,INT hlpmsg);
static VOID swhofwd(VOID);
static VOID startfwd(CHAR *to);
VOID ffcdone(SHORT rc);
VOID ffwdone(SHORT rc);
static VOID swhocpy(VOID);
static VOID startcpy(CHAR *to);
VOID fccdone(SHORT rc);
VOID fcpdone(SHORT rc);
VOID fdldone(SHORT rc);
VOID fmddone(SHORT rc);
VOID fgtnext(VOID (*whndun)(SHORT rc));
VOID fgtprev(VOID (*whndun)(SHORT rc));
VOID fgtnear(SHORT nearop,VOID (*whndun)(SHORT rc));
VOID sforto(VOID);
VOID fsndone(SHORT rc);
VOID sfindm(VOID);
VOID ssrchmnu(VOID);
VOID clrots(VOID);
VOID vwotsf(INT retstt);
GBOOL otflr(VOID);
VOID otfld(SHORT rc);
VOID sotadf(VOID);
VOID sotrmf(VOID);
VOID sotnmsg(VOID);
VOID sotatonl(VOID);
VOID sot2uonl(VOID);
VOID sotfuonl(VOID);
VOID sotsmsg(VOID);
VOID sotkwds(VOID);
VOID errhlp(INT ehmsg,INT repmt);
VOID slstwhat(VOID);
VOID slistyp(VOID);
VOID sstartl(VOID);
VOID stmsglst(VOID);
VOID nxtmil(VOID);
VOID lstxtd(GBOOL aborted);
VOID lstmrkd(SHORT rc);
VOID msglstd(GBOOL aborted);
VOID trcans(CHAR *stg,SHORT len);
VOID sqscfgm(VOID);
VOID clrqsc(VOID);
VOID vwqsf(VOID);
VOID sqsadf(VOID);
VOID sqsrmf(VOID);
VOID sqsnmsg(VOID);
VOID sqsatonl(VOID);
VOID sqs2uonl(VOID);
VOID sqsfuonl(VOID);
VOID sqssmsg(VOID);
VOID sqskwds(VOID);
VOID startscn(VOID);
VOID clrprv(VOID);
VOID scancbk(INT evt,INT rc);
VOID listcbk(INT evt,INT rc);
VOID swhatfor(VOID);
static VOID smodwht(VOID);
static VOID get4mod(SHORT rc);
static VOID moddone(SHORT rc);
static VOID sdelmsn(VOID);
static VOID deldone(SHORT rc);
VOID nunapv(SHORT rc);
VOID punapv(SHORT rc);
VOID sfopmnu(VOID);
VOID soprmnu(VOID);
VOID newforum(VOID);
GBOOL alcfsdc(VOID);
VOID cinifor(VOID);
VOID fsdcrt(VOID);
VOID fcrtprp(INT tmplt,INT emode);
VOID fcrtdun(GBOOL save);
INT fcrtvfy(INT fldnum,CHAR *answer);
VOID sfcrtnam(VOID);
VOID scrtform(VOID);
VOID gcrtfor(VOID);
VOID ccrtfor(VOID);
VOID gedtform(VOID);
VOID sedtform(VOID);
GBOOL alcfsde(VOID);
VOID fsdedt(VOID);
VOID sfsdchg(VOID);
VOID fedtprp(INT tmplt,INT emode);
VOID fedtdun(GBOOL save);
INT fedtvfy(INT fldnum,CHAR *answer);
VOID gedtfor(VOID);
VOID cedtfor(VOID);
VOID sedecho(VOID);
VOID listecho(VOID);
VOID sech2add(VOID);
VOID sech2del(VOID);
VOID edfhlp(CHAR *topic);
SHORT efhdone(SHORT edflgs);
VOID sfor2del(VOID);
VOID scnfdel(VOID);
VOID gdelfor(VOID);
VOID cdelfor(VOID);
VOID suid2set(VOID);
VOID scfgaccm(VOID);
VOID ssetdfac(VOID);
VOID smodloc(VOID);
VOID sforuid(VOID);
VOID sucpysrc(VOID);
VOID sucpdst(VOID);
GBOOL fpfnck(VOID);
static VOID retmain(VOID);
INT curfacc(VOID);
GBOOL fechoed(VOID);
struct fordef *getcfd(VOID);
const struct fordef *curfdef(VOID);
VOID showhlp(VOID);
VOID shwhlp2(SHORT aborted);
VOID cshowhlp(VOID);
CHAR *chcred(INT amt);
CHAR *plperk(INT amt);
CHAR *descptr(INT necho);
CHAR *zapbck(CHAR *dir);
CHAR *pflstr(SHORT pfnlvl);
GBOOL flstr(VOID);
VOID flstd(SHORT rc);
VOID faccnot(USHORT forum,CHAR *userid);

VOID
iniaafor(VOID)                     /* initialize ASCII/ANSI Forums module  */
{
     stlcpy(formib.descrp,gmdnam("galfor.mdf"),MNMSIZ);
     forstate=register_module(&formib);
     fortlc=ynopt(FORTLC);
     dclvda(sizeof(struct otscan)+sizeof(unsigned)*(maxqsf()-1));
}

GBOOL
forinp(VOID)                       /* Forums input handler                 */
{
     setmeup();
     if (usrptr->flags&INJOIP) {
          switch (usrptr->substt) {
          case XMTING:
               if (usrptr->flags&ABOIP) {
                    xmtdone(TRUE);
                    if (efvda->cflags&CKDCYC) {
                         return(TRUE);
                    }
                    break;
               }
               return(TRUE);
          case LISTING:
               if (usrptr->flags&ABOIP) {
                    (*efvda->whndun)(TRUE);
                    chkcyc();
                    if (efvda->cflags&CKDCYC) {
                         return(TRUE);
                    }
                    break;
               }
               return(TRUE);
          case MSGLST:
               if (usrptr->flags&ABOIP) {
                    msglstd(TRUE);
                    chkcyc();
                    if (efvda->cflags&CKDCYC) {
                         return(TRUE);
                    }
                    break;
               }
               return(TRUE);
          case SENDING:
          case RPLING:
          case AUTOLOF:
          case ALOFNOW:
          case FWDING:
          case COPYING:
          case DELING:
          case MODING:
          case CRDNEAR:
          case CREREAD:
          case CRDNEXT:
          case CRDPREV:
          case CMARKRD:
          case CINIFOR:
          case CRTFOR:
          case CREATING:
          case EDITING:
          case FDELING:
          case NAPVING:
          case PAPVING:
               return(TRUE);
          case FMAINM:
          case FMAINS:
               gostt(FMAINS);
               break;
          case FINFO:
               if (usrptr->flags&ABOIP) {
                    retmain();
                    break;
               }
               return(TRUE);
          case FTDSCN:
          case FTSCAN:
          case FDSCAN:
          case FSCAN:
               goscstt();
               break;
          case FREAD:
          case FAREAD:
          case FTREAD:
          case FOREAD:
               gordstt();
               break;
          case FOPRDA:
          case FOPRDU:
          case FOPRDX:
               gfoprdst();
               break;
          case FINDM:
          case FINDS:
               gostt(FINDS);
               break;
          case FOPMNU:
          case FOPMNS:
               gostt(FOPMNS);
               break;
          case OPRMNU:
          case OPRMNS:
               gostt(OPRMNS);
               break;
          default:
               gostt(usrptr->substt);
               break;
          }
          efvda->dftchr=getdft();
          outprf(usrnum);
          return(TRUE);
     }
     switch (usrptr->substt) {
     case XMTING:
          btuclo(usrnum);
          xmtdone(TRUE);
          if (!(efvda->cflags&CKDCYC)) {
               efvda->dftchr=getdft();
               outprf(usrnum);
          }
          return(TRUE);
     case FINFO:
          btuclo(usrnum);
          retmain();
          efvda->dftchr=getdft();
          outprf(usrnum);
          return(TRUE);
     case LISTING:
          btuclo(usrnum);
          (*efvda->whndun)(TRUE);
          chkcyc();
          if (!(efvda->cflags&CKDCYC)) {
               efvda->dftchr=getdft();
               outprf(usrnum);
          }
          return(TRUE);
     case MSGLST:
          btuclo(usrnum);
          msglstd(TRUE);
          chkcyc();
          if (!(efvda->cflags&CKDCYC)) {
               efvda->dftchr=getdft();
               outprf(usrnum);
          }
          return(TRUE);
     case NAPVING:
     case PAPVING:
          clsgmerq(efwork);
          btuclo(usrnum);
          prfmsg(ABOLST);
          outprf(usrnum);
          retmain();
          chkcyc();
          if (!(efvda->cflags&CKDCYC)) {
               efvda->dftchr=getdft();
               outprf(usrnum);
          }
          return(TRUE);
     case CRDNEXT:
     case CRDPREV:
          if (srchipg()) {
               clsgmerq(efwork);
               prfmsg(ABOSRCH);
               outprf(usrnum);
               retmain();
               chkcyc();
               if (!(efvda->cflags&CKDCYC)) {
                    efvda->dftchr=getdft();
                    outprf(usrnum);
               }
          }
     case CRDNEAR:
     case CREREAD:
     case CMARKRD:
     case DELING:
     case MODING:
     case CRDPRNT:
     case CINIFOR:
     case CRTFOR:
     case CREATING:
     case EDITING:
     case FDELING:
          return(TRUE);
     case AUTOLOF:
          slofwait();
     case ALOFNOW:
     case SENDING:
     case RPLING:
     case FWDING:
     case COPYING:
          return(TRUE);
     }
     if (efvda->cflags&DIDCYC) {
          rstcnc();
     }
     else if (usrptr->substt > 0 && fpfnck()) {
          return(TRUE);
     }
     else {
          chkdft(efvda->dftchr);
          if (margc == 1 && sameas(margv[0],"x")) {
               switch (usrptr->substt) {
               case FMAINM:
               case FMAINS:
                    return(FALSE);
               case FINDM:
               case FINDS:
               case WHATFOR:
               case MODWHT:
               case DELMSN:
               case FOPMNU:
               case FOPMNS:
               case OPRMNU:
               case OPRMNS:
                    retmain();
                    break;
               case SRCHMNU:
                    unrscan(usrnum);
                    usrscn[usrnum]=NULL;
               case QSCFGM:
               case LSTWHAT:
                    condex();
                    gostt(FINDS);
                    break;
               case OTADF:
               case OTRMF:
               case OTNMSG:
               case OTATONL:
               case OT2UONL:
               case OTFUONL:
               case OTSMSG:
               case OTKWDS:
                    gostt(SRCHMNU);
                    break;
               case QCADD:
               case QCDEL:
               case QSNMSG:
               case QSATONL:
               case QS2UONL:
               case QSFUONL:
               case QSSMSG:
               case QSKWDS:
                    gostt(QSCFGM);
                    break;
               case FCRTNAM:
               case CRTFORM:
               case EDTFMNU:
                    prfmsg(efvda->flags&CRTING ? FCRTABT : FEDTABT);
                    outprf(usrnum);
                    condex();
                    gostt(curfacc() >= SYAXES ? OPRMNS : FOPMNS);
                    break;
               case FOR2DEL:
                    condex();
                    gostt(OPRMNS);
                    break;
               case CNFDEL:
                    clsgmerq(efwork);
                    condex();
                    gostt(OPRMNS);
                    break;
               case EDECHO:
               case ECH2ADD:
               case ECH2DEL:
                    gostt(efvda->flags&CRTING ? CRTFORM : EDTFMNU);
                    break;
               case FSDCHG:
                    free(efvda->fsdspc);
                    free(efvda->fsdtmp);
                    gostt(EDTFMNU);
                    break;
               case CFGACCM:
               case UID2SET:
               case FORUID:
               case SYSUID:
               case FSETACH:
               case SSETACH:
               case UCPYSRC:
               case UCPDST:
               case UCPDST2:
                    condex();
                    gostt(curfacc() >= SYAXES ? OPRMNS : FOPMNS);
                    break;
               case SETDFAC:
               case MODLOC:
                    gostt(CFGACCM);
                    break;
               case STARTF:
               case STARTN:
               case FTDSCN:
               case FTSCAN:
               case FDSCAN:
               case FSCAN:
               case FSCBEG:
               case FSCBEGX:
               case FSCEND:
               case FSCENDX:
               case GONUM:
               case THRFBP:
               case DLNOW:
               case FREAD:
               case FAREAD:
               case FTREAD:
               case FOREAD:
               case USEQUO:
               case FAUTH:
               case FOPRDA:
               case FOPRDU:
               case FOPRDX:
               case WHOFWD:
               case WHOCPY:
               case CMTPMT:
               case FORMTO:
               case FORETO:
               case LISTYP:
               case STRTLST:
                    clsgmerq(efwork);
                    retmain();
                    break;
               case WHO2CC:
                    clrxrf();
                    gostt(POSTWRT);
                    break;
               case POSTWRT:
               case NAMATT:
                    clsgmerq(efwork);
                    if (efvda->cclist != NULL) {
                         free(efvda->cclist);
                         efvda->cclist=NULL;
                    }
                    if ((msg->flags&FILATT) && !(msg->flags&FILIND)) {
                         unlink(filatt);
                    }
                    retmain();
                    break;
            default:
#ifdef DEBUG
              catastro("Update your X list! (state=%d)",
                    usrptr->substt);
#endif
                    break;
               }
               efvda->dftchr=getdft();
               outprf(usrnum);
               return(TRUE);
          }
     }
     do {
          bgncnc();
          switch (usrptr->substt) {
          case 0:
               btumil(usrnum,DFTIMX);
               cncchr();
               peruflg[usrnum]&=~(INEMAIL|INEDIT|INFTF|INFSD|INEDHLP);
               entrfor();
               break;
          case FMAINM:
          case FMAINS:
               sfmainm();
               break;
          case STARTF:
          case STARTN:
               sstart(TRUE);
               break;
          case FTDSCN:
          case FTSCAN:
          case FDSCAN:
          case FSCAN:
          case FSCBEG:
          case FSCBEGX:
          case FSCEND:
          case FSCENDX:
               sfscan();
               break;
          case GONUM:
               sgonum();
               break;
          case THRFBP:
               sthrfbp();
               break;
          case DLNOW:
               sdlnow(fdndone);
               break;
          case FREAD:
          case FAREAD:
          case FTREAD:
          case FOREAD:
               sfread();
               break;
          case USEQUO:
               susequo();
               break;
          case FAUTH:
               sfauth();
               break;
          case FOPRDA:
          case FOPRDU:
          case FOPRDX:
               sforop();
               break;
          case WHOFWD:
               swhofwd();
               break;
          case WHOCPY:
               swhocpy();
               break;
          case CMTPMT:
               scmtpmt();
               break;
          case FORMTO:
          case FORETO:
               sforto();
               break;
          case POSTWRT:
               spostwrt();
               break;
          case NAMATT:
               snamatt();
               break;
          case WHO2CC:
               swhotocc();
               break;
          case FINDM:
          case FINDS:
               sfindm();
               break;
          case SRCHMNU:
               ssrchmnu();
               break;
          case OTADF:
               sotadf();
               break;
          case OTRMF:
               sotrmf();
               break;
          case OTNMSG:
               sotnmsg();
               break;
          case OTATONL:
               sotatonl();
               break;
          case OT2UONL:
               sot2uonl();
               break;
          case OTFUONL:
               sotfuonl();
               break;
          case OTSMSG:
               sotsmsg();
               break;
          case OTKWDS:
               sotkwds();
               break;
          case LSTWHAT:
               slstwhat();
               break;
          case LISTYP:
               slistyp();
               break;
          case STRTLST:
               sstartl();
               break;
          case QSCFGM:
               sqscfgm();
               break;
          case QCADD:
               sqsadf();
               break;
          case QCDEL:
               sqsrmf();
               break;
          case QSNMSG:
               sqsnmsg();
               break;
          case QSATONL:
               sqsatonl();
               break;
          case QS2UONL:
               sqs2uonl();
               break;
          case QSFUONL:
               sqsfuonl();
               break;
          case QSSMSG:
               sqssmsg();
               break;
          case QSKWDS:
               sqskwds();
               break;
          case WHATFOR:
               swhatfor();
               break;
          case MODWHT:
               smodwht();
               break;
          case DELMSN:
               sdelmsn();
               break;
          case FOPMNU:
          case FOPMNS:
               sfopmnu();
               break;
          case OPRMNU:
          case OPRMNS:
               soprmnu();
               break;
          case FCRTNAM:
               sfcrtnam();
               break;
          case CRTFORM:
               scrtform();
               break;
          case EDTFMNU:
               sedtform();
               break;
          case FSDCHG:
               sfsdchg();
               break;
          case EDECHO:
               sedecho();
               break;
          case ECH2ADD:
               sech2add();
               break;
          case ECH2DEL:
               sech2del();
               break;
          case FOR2DEL:
               sfor2del();
               break;
          case CNFDEL:
               scnfdel();
               break;
          case CFGACCM:
               scfgaccm();
               break;
          case SETDFAC:
               ssetdfac();
               break;
          case MODLOC:
               smodloc();
               break;
          case UID2SET:
               suid2set();
               break;
          case FORUID:
          case SYSUID:
          case FSETACH:
          case SSETACH:
               sforuid();
               break;
          case UCPYSRC:
               sucpysrc();
               break;
          case UCPDST:
          case UCPDST2:
               sucpdst();
               break;
          default:
               catastro("Invalid state in ASCII/ANSI forums (state=%d)",
                        usrptr->substt);
          }
     } while (!endcnc());
     efvda->dftchr=getdft();
     outprf(usrnum);
     return(TRUE);
}

VOID
forsts(VOID)                       /* Forums status handler                */
{
     if (status == CYCLE) {
          setmeup();
          efvda->cflags|=DIDCYC;
          switch (usrptr->substt) {
          case FINFO:
               cshowhlp();
               break;
          case LISTING:
               lister();
               break;
          case SENDING:
               sender();
               break;
          case RPLING:
               replyr();
               break;
          case AUTOLOF:
               clofwait();
          case ALOFNOW:
               break;
          case DELING:
               cdelm();
               break;
          case MODING:
               cmodr();
               break;
          case CRDPRNT:
               cgetprnt();
               break;
          case CRDNEAR:
               cgetnear();
               break;
          case CREREAD:
               creget();
               break;
          case CRDNEXT:
               cygetnext();
               break;
          case CRDPREV:
               cgetprev();
               break;
          case XMTING:
               cxmtxt();
               break;
          case CMARKRD:
               cmarkoff();
               break;
          case FWDING:
               cfwdr();
               break;
          case COPYING:
               ccopyr();
               break;
          case CINIFOR:
               cinifor();
               break;
          case CREATING:
               ccrtfor();
               break;
          case EDITING:
               cedtfor();
               break;
          case FDELING:
               cdelfor();
               break;
          case MSGLST:
               nxtmil();
               break;
          case NAPVING:
               getnext(nunapv);
               break;
          case PAPVING:
               getprev(punapv);
               break;
          default:
               efvda->cflags&=~DIDCYC;
               dfsthn();
               break;
          }
     }
     else if (status == OUTMT) {
          setmeup();
          switch (usrptr->substt) {
          case CRTFOR:
               btuoes(usrnum,FALSE);
               fcrtprp(CRFSDN,0);
               fsdrft();
               fsdego(fcrtvfy,fcrtdun);
               outprf(usrnum);
               peruflg[usrnum]|=INFSD;
               break;
          case EDTFOR:
               btuoes(usrnum,FALSE);
               fedtprp(EDFSDN2,0);
               fsdscb->flddat[EDATE].flags|=FFFAVD;
               fsdscb->flddat[EDATFIL].flags|=FFFAVD;
               fsdscb->flddat[EATTPATH].flags|=FFFAVD;
               fsdrft();
               fsdego(fedtvfy,fedtdun);
               outprf(usrnum);
               peruflg[usrnum]|=INFSD;
               break;
          }
     }
     else {
          dfsthn();
     }
}

VOID
forhup(VOID)                       /* Forums hangup handler                */
{
     setmeup();
     if (usrptr->state == forstate) {
          switch (usrptr->substt) {
          case 0:
          case FMAINM:
          case FMAINS:
          case FINFO:
          case AUTOLOF:
          case ALOFNOW:
          case FINDM:
          case FINDS:
          case SRCHMNU:
          case OTADF:
          case OTRMF:
          case OTNMSG:
          case OTATONL:
          case OT2UONL:
          case OTFUONL:
          case OTSMSG:
          case OTKWDS:
          case LSTWHAT:
          case QSCFGM:
          case QCADD:
          case QCDEL:
          case QSNMSG:
          case QSATONL:
          case QS2UONL:
          case QSFUONL:
          case QSSMSG:
          case QSKWDS:
          case WHATFOR:
          case MODWHT:
          case DELMSN:
          case FOPMNU:
          case FOPMNS:
          case OPRMNU:
          case OPRMNS:
          case FCRTNAM:
          case CRTFORM:
          case EDTFMNU:
          case EDECHO:
          case ECH2ADD:
          case ECH2DEL:
          case FOR2DEL:
          case CFGACCM:
          case SETDFAC:
          case MODLOC:
          case UID2SET:
          case FORUID:
          case SYSUID:
          case FSETACH:
          case SSETACH:
          case UCPYSRC:
          case UCPDST:
          case UCPDST2:
               break;
          case STARTF:
          case STARTN:
          case FTDSCN:
          case FTSCAN:
          case FDSCAN:
          case FSCAN:
          case FSCBEG:
          case FSCBEGX:
          case FSCEND:
          case FSCENDX:
          case GONUM:
          case THRFBP:
          case DLNOW:
          case FREAD:
          case FAREAD:
          case FTREAD:
          case FOREAD:
          case USEQUO:
          case FAUTH:
          case FOPRDA:
          case FOPRDU:
          case FOPRDX:
          case WHOFWD:
          case RAFABT:
          case WHOCPY:
          case CMTPMT:
          case FORMTO:
          case FORETO:
          case CNFDEL:
          case DELING:
          case MODING:
          case CRDPRNT:
          case CRDNEAR:
          case CRDNEXT:
          case CRDPREV:
          case XMTING:
          case CMARKRD:
          case FWDING:
          case COPYING:
          case LISTYP:
          case STRTLST:
          case CREATING:
          case EDITING:
          case FDELING:
          case MSGLST:
          case NAPVING:
          case PAPVING:
               clsgmerq(efwork);
               break;
          case CINIFOR:
               clsgmerq(efwork);
               free(efvda->fsdspc);
               free(efvda->fsdtmp);
               break;
          case CRTFOR:
               free(efvda->fsdspc);
               free(efvda->fsdtmp);
               break;
          case FSDCHG:
               free(efvda->fsdspc);
               free(efvda->fsdtmp);
               break;
          case LISTING:
               if (gmerqopn(efwork)) {
                    clsgmerq(efwork);
               }
               break;
          case POSTWRT:
          case NAMATT:
          case WHO2CC:
          case SENDING:
          case RPLING:
               clsgmerq(efwork);
               if (efvda->cclist != NULL) {
                    free(efvda->cclist);
                    efvda->cclist=NULL;
               }
               if ((msg->flags&FILATT) && !(msg->flags&FILIND)) {
                    unlink(filatt);
               }
               break;
          default:
#ifdef DEBUG
            catastro("Update Forums hangup states! (state=%d)",
               usrptr->substt);
#endif
               break;
          }
     }
     else {
          if (peruflg[usrnum]&INFSD) {
               free(efvda->fsdspc);
               free(efvda->fsdtmp);
          }
          if (peruflg[usrnum]&INEDHLP) {
               free(efvda->ccptr);
          }
     }
     if (usrscn[usrnum] != NULL) {
          unrscan(usrnum);
          usrscn[usrnum]=NULL;
     }
}

VOID
entrfor(VOID)                      /* enter Forums module (substt == 0)    */
{
     if (!fidxst(qsptr->curfor) || foracc(qsptr->curfor) == NOAXES) {
          qsptr->curfor=dftfor;
          if (!fidxst(qsptr->curfor) && foracc(qsptr->curfor=0U) == NOAXES) {
               catastro("Configuration option \"DFTFOR\" names a forum that "
                        "doesn't exist");
          }
     }
     joinfor(qsptr->curfor);
     gostt(FMAINM);
}

VOID
joinfor(                           /* join a Forum                         */
USHORT forum)                      /*   forum ID to join                   */
{
     qsptr->curfor=forum;
     usrptr->crdrat=curfdef()->ccr;
}

VOID
gfmain(                            /* go to main forum menu                */
GBOOL shortform)                   /*   show short form?                   */
{
     if (shortform) {
          if (curfacc() == SYAXES) {
               prfmsg(FSMAINS,curfnm(),curftpc());
          }
          else if (curfacc() >= OPAXES) {
               prfmsg(FOMAINS,curfnm(),curftpc());
          }
          else if (fortlc) {
               prfmsg(FTMAINS,curfnm(),curftpc());
          }
          else {
               prfmsg(FMAINS,curfnm(),curftpc());
          }
          usrptr->substt=FMAINS;
     }
     else {
          getcfd();
          if (curfacc() == SYAXES) {
               prfmsg(FSMAINM,curfnm(),curftpc(),fdef->nw4app);
          }
          else if (curfacc() >= OPAXES) {
               prfmsg(FOMAINM,curfnm(),curftpc(),fdef->nw4app);
          }
          else if (fortlc) {
               prfmsg(FTMAINM,curfnm(),curftpc());
          }
          else {
               prfmsg(FMAINM,curfnm(),curftpc());
          }
          usrptr->substt=FMAINM;
     }
}

VOID
sfmainm(VOID)                      /* state: main Forum menu               */
{
     switch (morcnc()) {
     case '?':
          cncall();
          if (usrptr->substt == FMAINM) {
               showhlp();
          }
          else {
               gostt(FMAINM);
          }
          break;
     case 'R':
          cncchr();
          inigmerq(efwork);
          efvda->tmpmid=firstnew(usaptr->userid,qsptr->curfor);
          inormrd(efwork,usaptr->userid,qsptr->curfor,efvda->tmpmid);
          efvda->cflags&=~NOCUR;
          getnear(GTLE,chk4new);
          break;
     case 'W':
          cncchr();
          if (curfacc() < WRAXES) {
               forerr(FNOWRT);
               retmain();
          }
          else if (!tstcrd(curfdef()->chgmsg)) {
               cncall();
               prfmsg(FNOCRD);
               howbuy();
               outprf(usrnum);
               retmain();
          }
          else {
               efvda->flags&=~CCLST;
               efvda->numcc=0;
               efvda->cclstsz=0U;
               ASSERT(efvda->cclist == NULL);
               setmem(msg,sizeof(struct message),0);
               msg->forum=qsptr->curfor;
               stlcpy(msg->from,usaptr->userid,MAXADR);
               inigmerq(efwork);
               if (fechoed()) {
                    gostt(FORETO);
               }
               else {
                    gostt(FORMTO);
               }
          }
          break;
     case 'F':
          cncchr();
          gostt(FINDM);
          break;
     case 'T':
          if ((fortlc || curfacc() >= OPAXES) && (tjoinrou != NULL)) {
               cncall();
               (*tjoinrou)((USHORT)FOR2CHAN(qsptr->curfor));
               clrprf();
          }
          else {
               forerr(CNOTIL);
               retmain();
          }
          break;
     case 'S':
          cncchr();
          if (*nxtcmd != '\0' && *nxtcmd != ' ') {
               usrptr->flags&=~CONCEX;
          }
          gostt(WHATFOR);
          break;
     default:
          if (curfacc() >= OPAXES) {
               switch (cncchr()) {
               case 'M':
                    prfmsg(MODWRN);
                    gostt(MODWHT);
                    break;
               case 'E':
                    gostt(DELMSN);
                    break;
               case 'A':
                    prfmsg(SCANAP);
                    outprf(usrnum);
                    clrprf();
                    efvda->cflags|=SCN4UA;
                    efvda->cflags&=~UATFND;
                    inigmerq(efwork);
                    inormrd(efwork,usaptr->userid,qsptr->curfor,LASTM);
                    getprev(punapv);
                    break;
               case 'O':
                    if (curfacc() >= SYAXES) {
                         gostt(OPRMNU);
                    }
                    else {
                         gostt(FOPMNU);
                    }
                    break;
               default:
                    forerr(CNOTIL);
                    retmain();
                    break;
               }
          }
          else {
               forerr(CNOTIL);
               retmain();
          }
     }
}

static VOID
forerr(                            /* display error message                */
INT msg)                           /*   message to display                 */
{
     cncall();
     clrinp();
     prfmsg(msg);
     outprf(usrnum);
}

static VOID
chk4new(                           /* check for new messages in the forum  */
SHORT rc)                            /*   GME read code                      */
{
     switch (rc) {
     case GMEOK:
          if (msg->msgid > efvda->tmpmid) {
               gostt(STARTN);
          }
          else {
               gostt(STARTF);
          }
          efvda->curmid=efvda->tmpmid;
          break;
     default:
          clsgmerq(efwork);
          cycall();
          switch (rc) {
          case GMENFND:
               prfmsg(NOFMSG);
               break;
          case GMECRD:
               prfmsg(FNRDCRD);
               howbuy();
               break;
          default:
               prfmsg(READERR,rc);
               break;
          }
          outprf(usrnum);
          retmain();
          break;
     }
}

VOID
sstart(                            /* state: which message to start with   */
GBOOL newflg)                      /*   is default "new message"?          */
{
     if (!startat(newflg,RTSHLP)) {
          prfmsg(NONEWF);
          outprf(usrnum);
          retmain();
     }
}

static VOID
sgonum(VOID)                       /* state: which message to go to        */
{
     if (!startat(FALSE,GNMHLP)) {
          errhlp(NOCURM,usrptr->substt);
     }
}

static GBOOL                       /*   returns FALSE if can't start scan  */
startat(                           /* start scanning at what message?      */
GBOOL newflg,                      /*   is current message "new"           */
INT hlpmsg)                        /*   help message to display            */
{
     switch (morcnc()) {
     case '.':
          cncchr();
          if (newflg && msg->msgid <= efvda->tmpmid) {
               efvda->curmid=0L;
               clsgmerq(efwork);
               cncall();
               return(FALSE);
          }
          if (efvda->cflags&NOCUR) {
               return(FALSE);
          }
          scan();
          break;
     case 'F':
          cncchr();
          jumpto(FIRSTM);
          break;
     case 'L':
          cncchr();
          jumpto(LASTM);
          break;
     case '?':
          errhlp(hlpmsg,usrptr->substt);
          break;
     default:
          if (isdigit(morcnc())) {
               efvda->tmpmid=cnclon();
               efvda->flags|=JUMPED;
               jumpto(efvda->tmpmid);
          }
          else {
               errhlp(FORHUH,usrptr->substt);
          }
          break;
     }
     return(TRUE);
}

static VOID
jumpto(                            /* jump to a specific message           */
LONG msgid)                      /*   message ID to jump to              */
{
     exithr();
     msgctx(efwork,msgid);
     if (usrscn[usrnum] != NULL) {
          seqctx(efwork,FSQFOR);
          clrprv();
          efvda->prvlst[0].fid=msg->forum;
     }
     getnear(LEGT,goscan);
}

VOID
goscan(                            /* start reading after finding msg      */
SHORT rc)                            /*   GME read code                      */
{
     switch (rc) {
     case GMEOK:
          if ((efvda->flags&JUMPED) && msg->msgid != efvda->tmpmid) {
               prfmsg(MISMSG,l2as(efvda->tmpmid));
          }
          efvda->flags&=~JUMPED;
          if (usrscn[usrnum] != NULL && efvda->prvidx == 0) {
               efvda->prvlst[0].fid=msg->forum;
               efvda->prvlst[0].mid=msg->msgid;
          }
          scan();
          break;
     default:
          clsgmerq(efwork);
          cycall();
          switch (rc) {
          case GMENFND:
               if (usrscn[usrnum] == NULL && !(efvda->cflags&SCN4UA)) {
                    prfmsg(NOFMSG);
               }
               else {
                    if (efvda->cflags&SCN4UA) {
                         prfmsg(SUOMGON);
                    }
                    else if (efvda->flags&QSCIPG) {
                         prfmsg(QSOMGON);
                    }
                    else {
                         prfmsg(OTOMGON);
                    }
               }
               break;
          case GMECRD:
               prfmsg(FNRDCRD);
               howbuy();
               break;
          default:
               prfmsg(READERR,rc);
               break;
          }
          outprf(usrnum);
          retmain();
          break;
     }
}

VOID
scan(VOID)                         /* scan messages to you                 */
{
     efvda->curmid=msg->msgid;
     efvda->cflags&=~NOCUR;
     if (isripu() || (qsptr->flags&CMBHDR)) {
          if (cb4hdr) {
               prf("\x0C");
          }
          sumams(SUMMSG,msg);
          outprf(usrnum);
          readit();
     }
     else {
          sumams(SUMMSG,msg);
          if (morcnc() != 'P') {
               outprf(usrnum);
          }
          goscstt();
     }
}

VOID
goscstt(VOID)                      /* go to pre-read (scan) state          */
{
     if (efvda->flags&THRING) {
          if (dnldok(msg->forum)) {
               gostt(FTDSCN);
          }
          else {
               gostt(FTSCAN);
          }
     }
     else {
          if (dnldok(msg->forum)) {
               gostt(FDSCAN);
          }
          else {
               gostt(FSCAN);
          }
     }
}

VOID
nxtscn(                            /* read after finding next message      */
SHORT rc)                            /*   GME read code                      */
{
     switch (rc) {
     case GMEOK:
          if (usrscn[usrnum] != NULL && !(efvda->flags&THRING)) {
               if (efvda->prvidx == 0) {
                    efvda->prvlst[0].fid=msg->forum;
                    efvda->prvlst[0].mid=msg->msgid;
               }
               else {
                    --efvda->prvidx;
               }
          }
          scan();
          break;
     case GMENFND:
          if (efvda->flags&THRING) {
               cycall();
               prfmsg(THREND);
               goscstt();
          }
          else if (usrscn[usrnum] != NULL) {
               if (efvda->prvidx > 0) {
                    --efvda->prvidx;
                    fgtnext(nxtscn);
               }
               else {
                    cycall();
                    clsgmerq(efwork);
                    if (efvda->flags&QSCIPG) {
                         prfmsg(QSCMPL);
                    }
                    else {
                         prfmsg(EOSRCH);
                    }
                    outprf(usrnum);
                    retmain();
               }
          }
          else {
               cycall();
               gostt(efvda->cflags&NOCUR ? FSCENDX : FSCEND);
          }
          break;
     default:
          clsgmerq(efwork);
          cycall();
          if (rc == GMECRD) {
               prfmsg(FNRDCRD);
               howbuy();
          }
          else {
               prfmsg(READERR,rc);
          }
          outprf(usrnum);
          retmain();
          break;
     }
}

VOID
prvscn(                            /* read after finding previous message  */
SHORT rc)                            /*   GME read code                      */
{
     switch (rc) {
     case GMEOK:
          if (usrscn[usrnum] != NULL && !(efvda->flags&THRING)) {
               ++efvda->prvidx;
          }
          scan();
          break;
     case GMENFND:
          cycall();
          if (efvda->flags&THRING) {
               prfmsg(THRBGN);
               goscstt();
          }
          else {
               if (usrscn[usrnum] != NULL) {
                    if (efvda->cflags&NOCUR) {
                         ++efvda->prvidx;
                    }
                    else {
                         inormrd(efwork,usaptr->userid,msg->forum,msg->msgid);
                    }
               }
               gostt(efvda->cflags&NOCUR ? FSCBEGX : FSCBEG);
          }
          break;
     default:
          clsgmerq(efwork);
          cycall();
          if (rc == GMECRD) {
               prfmsg(FNRDCRD);
               howbuy();
          }
          else {
               prfmsg(READERR,rc);
          }
          outprf(usrnum);
          retmain();
          break;
     }
}

static VOID
nocscn(                            /* read after find next msg (no current)*/
SHORT rc)                            /*   GME read code                      */
{
     if (usrscn[usrnum] != NULL) {
          nxtscn(rc);
          return;
     }
     switch (rc) {
     case GMEOK:
          if (msg->msgid == efvda->curmid) {
               cycall();
               gostt(FSCEND);
          }
          else if (msg->msgid < efvda->curmid) {
               cycall();
               efvda->cflags|=NOCUR;
               msgctx(efwork,efvda->curmid);
               gostt(FSCENDX);
          }
          else {
               scan();
          }
          break;
     default:
          clsgmerq(efwork);
          cycall();
          switch (rc) {
          case GMENFND:
               prfmsg(FSCNOM);
               break;
          case GMECRD:
               prfmsg(FNRDCRD);
               howbuy();
               break;
          default:
               prfmsg(READERR,rc);
               break;
          }
          outprf(usrnum);
          retmain();
          break;
     }
}

static VOID
nocscp(                            /* read after find prev msg (no current)*/
SHORT rc)                            /*   GME read code                      */
{
     if (usrscn[usrnum] != NULL) {
          prvscn(rc);
          return;
     }
     switch (rc) {
     case GMEOK:
          if (msg->msgid == efvda->curmid) {
               cycall();
               gostt(FSCBEG);
          }
          else if (msg->msgid > efvda->curmid) {
               cycall();
               efvda->cflags|=NOCUR;
               msgctx(efwork,efvda->curmid);
               gostt(FSCBEGX);
          }
          else {
               scan();
          }
          break;
     default:
          clsgmerq(efwork);
          cycall();
          switch (rc) {
          case GMENFND:
               prfmsg(FSCNOM);
               break;
          case GMECRD:
               prfmsg(FNRDCRD);
               howbuy();
               break;
          default:
               prfmsg(READERR,rc);
               break;
          }
          outprf(usrnum);
          retmain();
          break;
     }
}

VOID
sfscan(VOID)                       /* state: scanning Forum messages       */
{
     switch (cncchr()) {
     case 'N':
          hdlnext();
          break;
     case 'P':
          hdlprev();
          break;
     case 'T':
          gostt(THRFBP);
          break;
     case 'L':
          hdlleav();
          break;
     case 'D':
          if (dnldok(msg->forum)) {
               efdnload(fdndone);
          }
          else {
               errhlp(FORHUH,usrptr->substt);
          }
          break;
     case '#':
          gostt(GONUM);
          break;
     case '?':
          errhlp(FSCHLP,usrptr->substt);
          break;
     case 'R':
          if (!(efvda->cflags&NOCUR)) {
               readit();
               break;
          }
     default:
          errhlp(FORHUH,usrptr->substt);
          break;
     }
}

static VOID
sthrfbp(VOID)                      /* state: thread fwd/backwd/parent      */
{
     switch (cncchr()) {
     case 'F':
          entrthr();
          getnext(nxtscn);
          break;
     case 'B':
          entrthr();
          getprev(prvscn);
          break;
     case 'P':
          entrthr();
          getprnt(thrprnt);
          break;
     case '?':
          errhlp(FBPHLP,usrptr->substt);
          break;
     default:
          errhlp(FORHUH,usrptr->substt);
          break;
     }
}

static VOID
thrprnt(                           /* read after finding parent message    */
SHORT rc)                            /*   GME read code                      */
{
     switch (rc) {
     case GMEOK:
          scan();
          break;
     case GMENFND:
          cycall();
          prfmsg(NOPRNT);
          gordstt();
          break;
     default:
          clsgmerq(efwork);
          cycall();
          if (rc == GMECRD) {
               prfmsg(FNRDCRD);
               howbuy();
          }
          else {
               prfmsg(READERR,rc);
          }
          outprf(usrnum);
          retmain();
          break;
     }
}

static VOID
entrthr(VOID)                      /* start threading                      */
{
     if (efvda->prethr == 0L) {
          seqctx(efwork,FSQTHR);
          efvda->prethr=efvda->curmid;
          efvda->flags|=THRING;
     }
}

static VOID
exithr(VOID)                       /* restore stuff after threading        */
{
     if (efvda->prethr != 0L) {
          if (usrscn[usrnum] == NULL) {
               seqctx(efwork,FSQFOR);
          }
          else {
               seqctx(efwork,FSQSCN);
          }
          efvda->curmid=efvda->prethr;
          efvda->prethr=0L;
          msgctx(efwork,efvda->curmid);
          efvda->flags&=~THRING;
     }
}

static VOID
readit(VOID)                       /* read current message (etc.)          */
{
     xmtext(fmarkrd);
}

VOID
fmarkrd(                           /* start up mark-read process           */
SHORT rc)                            /*   (not used, for compatibility)      */
{
     (VOID)rc;
     markoff(fmrkdone);
}

VOID
fmrkdone(                          /* finished marking msg, continue read  */
SHORT rc)                            /*   result code from GME               */
{
     switch (rc) {
     case GMEOK:
          chkatt();
          break;
     default:
          clsgmerq(efwork);
          cycall();
          prfmsg(MARKERR,rc);
          outprf(usrnum);
          retmain();
          break;
     }
}

static VOID
chkatt(VOID)                       /* handle file attachment (if any)      */
{
     const struct fordef *tmpdef;

     if (msg->flags&FILATT) {
          if (fnd1st(&effb,dlname(msg),0)) {
               efvda->attsiz=effb.ff_fsize;
               if (dnldok(msg->forum)) {
                    tmpdef=curfdef();
                    if (tstcrd(tmpdef->chgadl
                              +tmpdef->chgdpk*(effb.ff_fsize/1024))) {
                         if (!(msg->flags&FILAPV)) {
                              prfmsg(ATTNAP);
                         }
                         gostt(DLNOW);
                         return;
                    }
                    else {
                         prfmsg(ATTCRD);
                         howbuy();
                    }
               }
               else if (msg->flags&FILAPV) {
                    prfmsg(FBNODL);
               }
               else {
                    prfmsg(ATTNAP);
               }
          }
          else {
               prfmsg(ATTNF,l2as(msg->msgid));
          }
     }
     gordstt();
}

VOID
fdndone(                           /* finished with Forum download         */
SHORT rc)                          /*   (not used, for compatibility)      */
{
     (VOID)rc;
     gordstt();
}

static VOID
gordstt(VOID)                      /* display appropriate post-read prompt */
{
     if (efvda->flags&THRING) {
          gostt(FTREAD);
     }
     else {
          if (foracc(msg->forum) >= OPAXES) {
               gostt(FOREAD);
          }
          else if (isauth()) {
               gostt(FAREAD);
          }
          else {
               gostt(FREAD);
          }
     }
}

VOID
sfread(VOID)                       /* state: E-mail post-read message      */
{
     switch (cncchr()) {
     case 'R':
          forrpl();
          break;
     case 'E':
          emlrpl();
          break;
     case 'A':
          if (isauth()) {
               gostt(FAUTH);
          }
          else {
               errhlp(FORHUH,usrptr->substt);
          }
          break;
     case 'F':
          if (foracc(msg->forum) >= OPAXES) {
               gfoprdst();
          }
          else {
               errhlp(FORHUH,usrptr->substt);
          }
          break;
     case 'T':
          gostt(THRFBP);
          break;
     case 'L':
          hdlleav();
          break;
     case 'N':
          hdlnext();
          break;
     case 'P':
          hdlprev();
          break;
     case '#':
          gostt(GONUM);
          break;
     case '?':
          errhlp(FRDHLP,usrptr->substt);
          break;
     default:
          errhlp(FORHUH,usrptr->substt);
          break;
     }
}

VOID
hdlnext(VOID)                      /* handle (N)ext selection              */
{
     if (efvda->flags&THRING) {
          exithr();
          prfmsg(EXITHR);
          outprf(usrnum);
          efvda->tmpmid=efvda->curmid;
          fgtnear(GTLE,nocscn);
     }
     else {
          fgtnext(nxtscn);
     }
}

VOID
hdlprev(VOID)                      /* handle (P)revious selection          */
{
     if (efvda->flags&THRING) {
          exithr();
          prfmsg(EXITHR);
          outprf(usrnum);
          efvda->tmpmid=efvda->curmid;
          if (usrscn[usrnum] != NULL) {
               efvda->cflags|=NOCUR;
          }
          fgtnear(LTGE,nocscp);
     }
     else {
          fgtprev(prvscn);
     }
}

VOID
hdlleav(VOID)                      /* handle (L)eave selection             */
{
     if (efvda->flags&THRING) {
          exithr();
          prfmsg(EXITHR);
          outprf(usrnum);
          efvda->tmpmid=efvda->curmid;
          efvda->flags|=JUMPED;
          fgtnear(GELT,goscan);
     }
     else {
          errhlp(FORHUH,usrptr->substt);
     }
}

static VOID
forrpl(VOID)                       /* start forum reply process            */
{
     switch (valadr(efwork,usaptr->userid,msg->from,msg->forum)) {
     case VALYES:
          msg->flags=0L;
          efvda->curmid=msg->msgid;
          startrpl(frdone);
          break;
     case VALNO:
          errhlp(RNOTAVL,usrptr->substt);
          break;
     case VALACC:
          errhlp(FNOWRT,usrptr->substt);
          break;
     case VALCRD:
          cncall();
          prfmsg(FNOCRD);
          howbuy();
          gostt(usrptr->substt);
          break;
     default:
          ASSERT(FALSE);
     }
}

static VOID
emlrpl(VOID)                       /* start E-mail reply process           */
{
     switch (valadr(efwork,usaptr->userid,msg->from,EMLID)) {
     case VALYES:
          if (islocal(msg->from) && !sameas(msg->from,"sysop")
           && ((struct usracc *)accbb->data)->credat > msg->crdate) {
               errhlp(UIDGON,usrptr->substt);
          }
          else {
               msg->forum=EMLID;
               msg->flags=0L;
               efvda->curmid=msg->msgid;
               startrpl(frdone);
          }
          break;
     case VALNO:
          if (islocal(msg->from)) {
               errhlp(UIDGON,usrptr->substt);
          }
          else {
               errhlp(RNOTAVL,usrptr->substt);
          }
          break;
     case VALACC:
          errhlp(NOWACC,usrptr->substt);
          break;
     case VALCRD:
          cncall();
          prfmsg(NOWCRD);
          howbuy();
          gostt(usrptr->substt);
          break;
     default:
          ASSERT(FALSE);
     }
}

VOID
frdone(                            /* done replying to forum message       */
SHORT rc)                          /*   was edit aborted?                  */
{
     if (!rc) {
          inigmerq(efwork);
     }
     if (usrscn[usrnum] == NULL) {
          inormrd(efwork,usaptr->userid,qsptr->curfor,efvda->curmid);
          if (efvda->flags&THRING) {
               prfmsg(EXITHR);
               exithr();
          }
          fgtnear(GTLE,nocscn);
     }
     else {
          if (efvda->flags&THRING) {
               prfmsg(EXITHR);
               efvda->curmid=efvda->prethr;
               efvda->prethr=0L;
               efvda->flags&=~THRING;
          }
          setscan(efwork,usrscn[usrnum]);
          setgmecb(efwork,scancbk);
          inormrd(efwork,usaptr->userid,efvda->prvlst[0].fid,efvda->curmid);
          fgtnext(nxtscn);
     }
}

VOID
sfauth(VOID)                       /* state: author options prompt         */
{
     authop(cncchr(),FAUTHLP);
}

VOID
gfoprdst(VOID)                     /* show forum-op post-read prompt       */
{
     if ((msg->flags&FILATT) && !(msg->flags&FILAPV)) {
          gostt(FOPRDA);
     }
     else if (msg->flags&EXEMPT) {
          gostt(FOPRDU);
     }
     else {
          gostt(FOPRDX);
     }
}

VOID
sforop(VOID)                       /* state: forum-op post-read options    */
{
     CHAR c;

     switch (c=cncchr()) {
     case 'A':
          if ((msg->flags&FILATT) && !(msg->flags&FILAPV)) {
               if (aprvmsg(efwork,msg,msgtxt,TRUE) == GMEOK) {
                    prfmsg(APVCNF);
               }
               gordstt();
          }
          else {
               errhlp(FORHUH,usrptr->substt);
          }
          break;
     case 'F':
          if (chkcfl(efwork)) {
               errhlp(USING,usrptr->substt);
          }
          else {
               gostt(WHOFWD);
          }
          break;
     case 'C':
          gostt(WHOCPY);
          break;
     case 'T':
          if (msg->flags&EXEMPT) {
               errhlp(FORHUH,usrptr->substt);
          }
          else {
               if (exmtmsg(efwork,msg,msgtxt,TRUE) == GMEOK) {
                    prfmsg(XMPCNF);
               }
               gordstt();
          }
          break;
     case 'U':
          if (msg->flags&EXEMPT) {
               if (exmtmsg(efwork,msg,msgtxt,FALSE) == GMEOK) {
                    prfmsg(UXMCNF);
               }
               gordstt();
          }
          else {
               errhlp(FORHUH,usrptr->substt);
          }
          break;
     default:
          authop(c,FOPRDH);
          break;
     }
}

VOID
authop(                            /* process common author/forum-op stuff */
CHAR c,                            /*   character to process               */
INT hlpmsg)                        /*   help message to display            */
{
     switch (c) {
     case 'E':
          deletem(fdldone);
          break;
     case 'M':
          if (chkcfl(efwork)) {
               errhlp(USING,usrptr->substt);
          }
          else if ((msg->flags&ISMPTR) && !(msg->flags&ISTCPY)) {
               errhlp(NMODHDR,usrptr->substt);
          }
          else {
               startmod(fmddone);
          }
          break;
     case 'N':
          hdlnext();
          break;
     case 'P':
          hdlprev();
          break;
     case '#':
          gostt(GONUM);
          break;
     case '?':
          errhlp(hlpmsg,usrptr->substt);
          break;
     default:
          errhlp(FORHUH,usrptr->substt);
          break;
     }
}

static VOID
swhofwd(VOID)                      /* state: to whom to forward message    */
{
     CHAR tmpadr[MAXADR];

     stlcpy(tmpadr,cncall(),MAXADR);
     if (sameas(tmpadr,"?")) {
          clrxrf();
          cfhlp();
          gostt(usrptr->substt);
          return;
     }
     if (sameas(tmpadr,"/?")) {
          clrxrf();
          listfor(usrptr->substt);
          return;
     }
     if (isdlst(tmpadr)) {
          clrxrf();
          errhlp(NCFLST,usrptr->substt);
          return;
     }
     if (islocal(tmpadr)) {
          switch (hdluid(tmpadr)) {
          case UIDFND:
               stlcpy(tmpadr,uidxrf.userid,MAXADR);
               break;
          case UIDPMT:
               gostt(usrptr->substt);
               return;
          case UIDCAL:
               return;
          default:
               ASSERT(FALSE);
          }
     }
     clrxrf();
     startfwd(tmpadr);
}

static VOID
startfwd(                          /* start forwarding process (if able)   */
CHAR *to)                           /*   address to forward to              */
{
     if (isforum(to)) {
          msg->flags&=~PRIMSG;
     }
     switch (vfwdadr(efwork,usaptr->userid,to,EMLID)) {
     case VALYES:
          break;
     case VALNO:
          errhlp(CFNSUCH,usrptr->substt);
          return;
     case VALACC:
          errhlp(CFNOAC,usrptr->substt);
          return;
     case VALCRD:
          cncall();
          prfmsg(CFNCRD);
          howbuy();
          gostt(usrptr->substt);
          return;
     default:
          ASSERT(FALSE);
     }
     if (msg->flags&FILATT) {
          switch (vfwdatt(efwork,usaptr->userid,to,EMLID)) {
          case VALYES:
               break;
          case VALNO:
          case VALACC:
               if (isforum(to)) {
                    errhlp(CFNUPL,usrptr->substt);
               }
               else {
                    errhlp(CFNOAC,usrptr->substt);
               }
               return;
          case VALCRD:
               cncall();
               prfmsg(CFNCRD);
               howbuy();
               gostt(usrptr->substt);
               return;
          default:
               ASSERT(FALSE);
          }
     }
     ASSERT(!(msg->flags&PRIMSG));
     ASSERT(!(msg->flags&RECREQ));
     efvda->tmpmid=msg->msgid;
     msg->forum=EMLID;
     stlcpy(msg->to,to,MAXADR);
     comment(ffcdone);
}

VOID
ffcdone(                           /* forums done adding comments when fwd */
SHORT rc)                          /*   TRUE if user aborted edit          */
{
     usrptr->state=forstate;
     if (rc) {
          fgtnear(GTLE,nocscn);
     }
     else {
          fwdit(ffwdone);
     }
}

VOID
ffwdone(                           /* forums done forwarding               */
SHORT rc)                            /*   result code from forward           */
{
     switch (rc) {
     case GMEAFWD:
     case GMEOK:
          fnotify(rc);
          break;
     case GMEUSE:
          prfmsg(USING);
          outprf(usrnum);
          break;
     case GMEACC:
          prfmsg(CFNOAC);
          outprf(usrnum);
          break;
     case GMECRD:
          prfmsg(CFNCRD);
          howbuy();
          outprf(usrnum);
          break;
     default:
          prfmsg(FWDERR,rc);
          outprf(usrnum);
          break;
     }
     clrprf();
     fgtnear(GTLE,nocscn);
}

static VOID
swhocpy(VOID)                      /* state: to whom to copy message       */
{
     CHAR tmpadr[MAXADR];

     stlcpy(tmpadr,cncall(),MAXADR);
     if (sameas(tmpadr,"?")) {
          clrxrf();
          cfhlp();
          gostt(usrptr->substt);
          return;
     }
     if (sameas(tmpadr,"/?")) {
          clrxrf();
          listfor(usrptr->substt);
          return;
     }
     if (isdlst(tmpadr)) {
          clrxrf();
          errhlp(NCFLST,usrptr->substt);
          return;
     }
     if (islocal(tmpadr)) {
          switch (hdluid(tmpadr)) {
          case UIDFND:
               stlcpy(tmpadr,uidxrf.userid,MAXADR);
               break;
          case UIDPMT:
               gostt(usrptr->substt);
               return;
          case UIDCAL:
               return;
          default:
               ASSERT(FALSE);
          }
     }
     clrxrf();
     startcpy(tmpadr);
}

static VOID
startcpy(                          /* start copying process (if able)      */
CHAR *to)                          /*   address to forward to              */
{
     if (isforum(to)) {
          msg->flags&=~PRIMSG;
     }
     switch (valadr(efwork,usaptr->userid,to,EMLID)) {
     case VALYES:
          break;
     case VALNO:
          errhlp(CFNSUCH,usrptr->substt);
          return;
     case VALACC:
          errhlp(CFNOAC,usrptr->substt);
          return;
     case VALCRD:
          cncall();
          prfmsg(CFNCRD);
          howbuy();
          gostt(usrptr->substt);
          return;
     default:
          ASSERT(FALSE);
     }
     if (msg->flags&FILATT) {
          switch (valatt(efwork,usaptr->userid,to,EMLID)) {
          case VALYES:
               break;
          case VALNO:
          case VALACC:
               if (isforum(to)) {
                    errhlp(CFNUPL,usrptr->substt);
               }
               else {
                    errhlp(CFNOAC,usrptr->substt);
               }
               return;
          case VALCRD:
               cncall();
               prfmsg(CFNCRD);
               howbuy();
               gostt(usrptr->substt);
               return;
          default:
               ASSERT(FALSE);
          }
     }
     ASSERT(!(msg->flags&PRIMSG));
     ASSERT(!(msg->flags&RECREQ));
     efvda->tmpmid=msg->msgid;
     msg->forum=EMLID;
     stlcpy(msg->to,to,MAXADR);
     comment(fccdone);
}

VOID
fccdone(                           /* forums done adding comments when cpy */
SHORT rc)                          /*   TRUE if user aborted edit          */
{
     usrptr->state=forstate;
     if (rc) {
          fgtnear(GTLE,nocscn);
     }
     else {
          copyit(fcpdone);
     }
}

VOID
fcpdone(                           /* forums done copying                  */
SHORT rc)                          /*   result code from copy              */
{
     switch (rc) {
     case GMEAFWD:
     case GMEOK:
          cnotify(rc);
          break;
     case GMEACC:
          prfmsg(CFNOAC);
          outprf(usrnum);
          break;
     case GMECRD:
          prfmsg(CFNCRD);
          howbuy();
          outprf(usrnum);
          break;
     default:
          prfmsg(COPYERR,rc);
          outprf(usrnum);
          break;
     }
     clrprf();
     fgtnear(GTLE,nocscn);
}

VOID
fdldone(                           /* done deleting a Forum message        */
SHORT rc)                            /*   GME result code                    */
{
     switch (rc) {
     case GMEOK:
          prfmsg(OKGONE,l2as(efvda->curmid));
          outprf(usrnum);
          break;
     case GMEUSE:
          prfmsg(USING);
          outprf(usrnum);
          break;
     default:
          cycall();
          prfmsg(DELERR,rc);
          break;
     }
     fgtnear(GTLE,nocscn);
}

VOID
fmddone(                           /* done modifying a Forum message       */
SHORT rc)                            /*   GME result code                    */
{
     switch (rc) {
     case GMEAGAIN:                /* user aborted edit                    */
          break;
     case GMEOK:
          prfmsg(SENTOK,l2as(efvda->curmid));
          outprf(usrnum);
          break;
     case GMEUSE:
          prfmsg(USING);
          outprf(usrnum);
          break;
     default:
          prfmsg(MODERR,rc);
          outprf(usrnum);
          break;
     }
     fgtnext(nxtscn);
}

VOID
fgtnext(                           /* forums read next msg during scan     */
VOID (*whndun)(SHORT rc))            /*   function to call when finished     */
{
     if (usrscn[usrnum] == NULL) {
          if (efvda->cflags&SCN4UA) {
               prfmsg(SCANAP);
               outprf(usrnum);
               getprev(punapv);
          }
          else {
               getnext(whndun);
          }
     }
     else {
          if (efvda->prvidx == 0) {
               msgctx(efwork,efvda->prvlst[0].mid);
               seqctx(efwork,FSQSCN);
               movmem(&efvda->prvlst[0],&efvda->prvlst[1],
                      sizeof(struct formid)*(MAXPRV-1));
               getnext(whndun);
          }
          else {
               inormrd(efwork,usaptr->userid,
                       efvda->prvlst[efvda->prvidx-1].fid,
                       efvda->prvlst[efvda->prvidx-1].mid);
               reget(whndun);
          }
     }
}

VOID
fgtprev(                           /* forums read previous msg during scan */
VOID (*whndun)(SHORT rc))            /*   function to call when finished     */
{
     if (usrscn[usrnum] == NULL) {
          if (efvda->cflags&SCN4UA) {
               prfmsg(SCANAP);
               outprf(usrnum);
               getnext(nunapv);
          }
          else {
               getprev(whndun);
          }
     }
     else {
          if (efvda->curmid != efvda->prvlst[(SHORT)efvda->prvidx].mid) {
               efvda->cflags|=NOCUR;
          }
          if (efvda->prvidx < MAXPRV-1
           && efvda->prvlst[efvda->prvidx+1].mid != 0L) {
               inormrd(efwork,usaptr->userid,
                       efvda->prvlst[efvda->prvidx+1].fid,
                       efvda->prvlst[efvda->prvidx+1].mid);
               reget(whndun);
          }
          else {
               (*whndun)(GMENFND);
          }
     }
}

VOID
fgtnear(                           /* forums read near msg during scan     */
SHORT nearop,                        /*   get near "style" to use            */
VOID (*whndun)(SHORT rc))            /*   function to call when finished     */
{
     if (usrscn[usrnum] == NULL) {
          if (efvda->cflags&SCN4UA) {
               efvda->nearop=nearop;
               switch (efvda->nearop) {
               case GTLE:
                    prfmsg(SCANAP);
                    outprf(usrnum);
                    getprev(punapv);
                    break;
               case LTGE:
                    prfmsg(SCANAP);
                    outprf(usrnum);
                    getnext(nunapv);
                    break;
               case LEGT:
               case GELT:
                    seqctx(efwork,FSQFOR);
                    reget(whndun);
                    break;
               default:
                    ASSERT(FALSE);
               }
          }
          else {
               getnear(nearop,whndun);
          }
     }
     else {
          efvda->nearop=nearop;
          switch (efvda->nearop) {
          case GTLE:
               fgtnext(whndun);
               break;
          case LTGE:
               fgtprev(whndun);
               break;
          case LEGT:
          case GELT:
               seqctx(efwork,FSQFOR);
               reget(whndun);
               break;
          default:
               ASSERT(FALSE);
          }
     }
}

VOID
sforto(VOID)                       /* state: to whom to direct forum msg   */
{
     stlcpy(msg->to,cncall(),MAXADR);
     if (sameas(msg->to,"?")) {
          clrxrf();
          if (fechoed()) {
               errhlp(FTEHLP,usrptr->substt);
          }
          else {
               errhlp(FTMHLP,usrptr->substt);
          }
          return;
     }
     if (sameas(msg->to,".")) {
          clrxrf();
          *msg->to='\0';
     }
     else if (msg->to[0] == '@' && fechoed()) {
          clrxrf();
          movmem(&msg->to[1],msg->to,strlen(msg->to));
     }
     else {
          switch (hdluid(msg->to)) {
          case UIDFND:
               stlcpy(msg->to,uidxrf.userid,MAXADR);
               break;
          case UIDPMT:
               gostt(usrptr->substt);
               return;
          case UIDCAL:
               return;
          default:
               ASSERT(FALSE);
          }
     }
     clrxrf();
     switch (valadr(efwork,msg->from,msg->to,qsptr->curfor)) {
     case VALYES:
          startwrt(fsndone);
          break;
     case VALNO:
          errhlp(NOSUCH,usrptr->substt);
          break;
     case VALACC:
          clsgmerq(efwork);
          prfmsg(FNOWRT);
          outprf(usrnum);
          retmain();
          break;
     case VALCRD:
          clsgmerq(efwork);
          prfmsg(FNOCRD);
          howbuy();
          outprf(usrnum);
          retmain();
          break;
     default:
          ASSERT(FALSE);
     }
}

VOID
fsndone(                           /* done writing a forum message         */
SHORT rc)                          /*   was edit aborted?                  */
{
     if (rc) {
          clsgmerq(efwork);
     }
     retmain();
}

VOID
sfindm(VOID)                       /* state: find menu                     */
{
     struct otscan *tmpscn;

     switch (cncchr()) {
     case 'S':
          ASSERT(usrscn[usrnum] == NULL);
          tmpscn=rsvscan(usrnum);
          if (tmpscn == NULL) {
               errhlp(NOSCNOW,FINDS);
          }
          else {
               usrscn[usrnum]=tmpscn;
               clrots();
               addf2ots(tmpscn,qsptr->curfor);
               if (morcnc()) {
                    gostt(SRCHMNU);
               }
               else {
                    vwotsf(SRCHMNU);
               }
          }
          break;
     case 'L':
          gostt(LSTWHAT);
          break;
     case 'Q':
          ASSERT(usrscn[usrnum] == NULL);
          tmpscn=rsvscan(usrnum);
          if (tmpscn == NULL) {
               errhlp(NOSCNOW,FINDS);
          }
          else {
               usrscn[usrnum]=tmpscn;
               qsc2ots(qsptr,tmpscn);
               if (tmpscn->nforums == 0) {
                    prfmsg(NOQSF);
                    outprf(usrnum);
                    retmain();
               }
               else {
                    efvda->flags|=QSCIPG;
                    startscn();
               }
          }
          break;
     case 'C':
          if (!morcnc()) {
               vwqsf();
          }
          gostt(QSCFGM);
          break;
     case '?':
          if (usrptr->substt == FINDS) {
               gostt(FINDM);
          }
          else {
               errhlp(FNDMHLP,FINDM);
          }
          break;
     default:
          errhlp(CNOTIL,FINDS);
          break;
     }
}

VOID
ssrchmnu(VOID)                     /* state: configure search menu         */
{
     switch (cncchr()) {
     case '?':
          errhlp(OTSHLP,usrptr->substt);
          break;
     case 'R':
          if (usrscn[usrnum]->flags&SCALL || usrscn[usrnum]->nforums > 0) {
               efvda->flags&=~QSCIPG;
               startscn();
          }
          else {
               prfmsg(NOQSF);
               outprf(usrnum);
               retmain();
          }
          break;
     case 'C':
          clrots();
          gostt(usrptr->substt);
          break;
     case 'V':
          cncall();
          vwotsf(usrptr->substt);
          break;
     case '+':
          gostt(OTADF);
          break;
     case '-':
          gostt(OTRMF);
          break;
     case 'K':
          gostt(OTKWDS);
          break;
     case 'N':
          gostt(OTNMSG);
          break;
     case 'A':
          gostt(OTATONL);
          break;
     case 'T':
          gostt(OT2UONL);
          break;
     case 'F':
          gostt(OTFUONL);
          break;
     case 'M':
          gostt(OTSMSG);
          break;
     default:
          errhlp(CNOTIL,usrptr->substt);
          break;
     }
}

VOID
clrots(VOID)                       /* clear one-time search                */
{
     setmem(usrscn[usrnum],sizeof(struct otscan)+sizeof(USHORT)*(MAXQSF-1),0);
}

VOID
vwotsf(                            /* view forums in one-time search       */
INT retstt)                        /*   substate to go to when done        */
{
     if (usrscn[usrnum]->flags&SCALL) {
          *efvda->tmpadr='\0';
     }
     else {
          efvda->count=0;
     }
     efvda->savstt=retstt;
     prfmsg(OTVWF);
     startlst(otflr,otfld);
}

GBOOL                              /*   returns FALSE if done              */
otflr(VOID)                        /* forums-in-one-time-search lister     */
{
     SHORT i;
     const struct fordef *fdp;
     struct otscan *ots;

     ots=usrscn[usrnum];
     if (ots->flags&SCALL) {
          if (nxtdefp(efvda->tmpadr) != NULL) {
               i=0;
               while (i < 4 && (fdp=nxtdefp(efvda->tmpadr)) != NULL) {
                    if (foracc(fdp->forum) > NOAXES) {
                         if (scnfidx(ots,fdp->forum) == NOIDX) {
                              prfmsg(OTSLST,fdp->name);
                              ++i;
                         }
                    }
                    stlcpy(efvda->tmpadr,fdp->name,FNMSIZ);
               }
               prf("\r");
               return(TRUE);
          }
          return(FALSE);
     }
     if (efvda->count < ots->nforums) {
          for (i=0 ; i < 4 && efvda->count < ots->nforums ; ++i,++efvda->count) {
               prfmsg(OTSLST,getfnm(ots->forlst[efvda->count]));
          }
          prf("\r");
          return(TRUE);
     }
     return(FALSE);
}

VOID
otfld(                             /* done listing forums in one-time srch */
SHORT rc)                            /*   (not used, for compatibility)      */
{
     (VOID)rc;
     gostt(efvda->savstt);
}

VOID
sotadf(VOID)                       /* state: add forum to search           */
{
     SHORT i;
     USHORT forum;
     CHAR *fornam;
     struct otscan *ots;

     ots=usrscn[usrnum];
     switch (morcnc()) {
     case '?':
          listfor(usrptr->substt);
          break;
     case '/':
          cncchr();
     default:
          fornam=cncwrd();
          if (sameas(fornam,"all")) {
               ots->nforums=0;
               ots->flags|=SCALL;
               gostt(SRCHMNU);
          }
          else {
               forum=getfid(fornam);
               if (forum != EMLID && foracc(forum) > NOAXES) {
                    if (ots->flags&SCALL) {
                         i=scnfidx(ots,forum);
                         if (i != NOIDX) {
                              --ots->nforums;
                              movmem(&ots->forlst[i+1],&ots->forlst[i],
                                     sizeof(USHORT)*(ots->nforums-i));
                         }
                    }
                    else {
                         if (ots->nforums < MAXQSF) {
                              if (scnfidx(ots,forum) == NOIDX) {
                                   addf2ots(ots,forum);
                              }
                         }
                         else {
                              prfmsg(OTLFUL);
                         }
                    }
                    gostt(SRCHMNU);
               }
               else {
                    prfmsg(FNXST);
                    gostt(usrptr->substt);
               }
          }
     }
}

VOID
sotrmf(VOID)                       /* state: forum to delete from search   */
{
     CHAR *fornam;
     SHORT i;
     USHORT forum;
     struct otscan *ots;

     ots=usrscn[usrnum];
     switch (morcnc()) {
     case '?':
          cncall();
          vwotsf(usrptr->substt);
          break;
     case '/':
          cncchr();
     default:
          fornam=cncwrd();
          if (sameas(fornam,"all")) {
               ots->nforums=0;
               ots->flags&=~SCALL;
               gostt(SRCHMNU);
          }
          else {
               forum=getfid(fornam);
               if (forum != EMLID) {
                    if (ots->flags&SCALL) {
                         if (scnfidx(ots,forum) != NOIDX) {
                              prfmsg(OTFNINL,fornam);
                         }
                         else if (faccok(forum)) {
                              addf2ots(ots,forum);
                         }
                    }
                    else {
                         i=scnfidx(ots,forum);
                         if (i == NOIDX) {
                              prfmsg(OTFNINL,fornam);
                         }
                         else {
                              --ots->nforums;
                              movmem(&ots->forlst[i+1],&ots->forlst[i],
                                     sizeof(USHORT)*(ots->nforums-i));
                         }
                    }
               }
               else {
                    prfmsg(OTFNINL,fornam);
               }
               gostt(SRCHMNU);
          }
     }
}

VOID
sotnmsg(VOID)                      /* state: new messages only in search?  */
{
     switch (cncyesno()) {
     case 'Y':
          usrscn[usrnum]->flags|=SCNEW;
          gostt(SRCHMNU);
          break;
     case 'N':
          usrscn[usrnum]->flags&=~SCNEW;
          gostt(SRCHMNU);
          break;
     case '?':
          errhlp(OTNMSGH,usrptr->substt);
          break;
     default:
          errhlp(YORN,usrptr->substt);
          break;
     }
}

VOID
sotatonl(VOID)                     /* state: msgs w/atts only in search?   */
{
     switch (cncyesno()) {
     case 'Y':
          usrscn[usrnum]->flags|=SCATT;
          gostt(SRCHMNU);
          break;
     case 'N':
          usrscn[usrnum]->flags&=~SCATT;
          gostt(SRCHMNU);
          break;
     case '?':
          errhlp(OTATONH,usrptr->substt);
          break;
     default:
          errhlp(YORN,usrptr->substt);
          break;
     }
}

VOID
sot2uonl(VOID)                     /* state: msgs to you only in search?   */
{
     switch (cncyesno()) {
     case 'Y':
          usrscn[usrnum]->flags|=SCTOU;
          gostt(SRCHMNU);
          break;
     case 'N':
          usrscn[usrnum]->flags&=~SCTOU;
          gostt(SRCHMNU);
          break;
     case '?':
          errhlp(OT2UONH,usrptr->substt);
          break;
     default:
          errhlp(YORN,usrptr->substt);
          break;
     }
}

VOID
sotfuonl(VOID)                     /* state: msgs from you only in search? */
{
     switch (cncyesno()) {
     case 'Y':
          usrscn[usrnum]->flags|=SCFRU;
          gostt(SRCHMNU);
          break;
     case 'N':
          usrscn[usrnum]->flags&=~SCFRU;
          gostt(SRCHMNU);
          break;
     case '?':
          errhlp(OTFUONH,usrptr->substt);
          break;
     default:
          errhlp(YORN,usrptr->substt);
          break;
     }
}

VOID
sotsmsg(VOID)                      /* state: message # to start search at  */
{
     if (morcnc() == '?') {
          errhlp(OTSMSGH,usrptr->substt);
     }
     else if (isdigit(morcnc())) {
          usrscn[usrnum]->stmsgid=cnclon();
          gostt(SRCHMNU);
     }
     else {
          errhlp(FORHUH,usrptr->substt);
     }
}

VOID
sotkwds(VOID)                      /* state: enter search keywords         */
{
     CHAR *inp;

     inp=cncall();
     if (sameas(inp,"?")) {
          errhlp(OTKWDSH,usrptr->substt);
     }
     else {
          stlcpy(usrscn[usrnum]->keywds,inp,MAXSKWD);
          gostt(SRCHMNU);
     }
}

VOID
slstwhat(VOID)                     /* state: list messages from where?     */
{
     struct otscan *tmpscn;

     switch (cncchr()) {
     case 'F':
          inigmerq(efwork);
          inormrd(efwork,usaptr->userid,qsptr->curfor,FIRSTM);
          gostt(LISTYP);
          break;
     case 'Q':
          ASSERT(usrscn[usrnum] == NULL);
          tmpscn=rsvscan(usrnum);
          if (tmpscn == NULL) {
               errhlp(NOSCNOW,FINDS);
          }
          else {
               usrscn[usrnum]=tmpscn;
               qsc2ots(qsptr,tmpscn);
               if (tmpscn->nforums == 0) {
                    prfmsg(NOQSF);
                    outprf(usrnum);
                    retmain();
               }
               else {
                    clrprv();
                    efvda->prvlst[0].fid=fstscnf(usaptr->userid,tmpscn);
                    inigmerq(efwork);
                    setgmecb(efwork,listcbk);
                    setscan(efwork,tmpscn);
                    inormrd(efwork,usaptr->userid,efvda->prvlst[0].fid,
                          fstscnm(usaptr->userid,tmpscn,efvda->prvlst[0].fid));
                    seqctx(efwork,FSQSCN);
                    gostt(LISTYP);
               }
          }
          break;
     default:
          errhlp(CNOTIL,usrptr->substt);
          break;
     }
}

VOID
slistyp(VOID)                      /* state: list brief/titles/full        */
{
     CHAR c;
     switch (c=cncchr()) {
     case 'B':
          efvda->flags|=LSTBRF;
          efvda->flags&=~LSTFUL;
          break;
     case 'T':
          efvda->flags&=~(LSTFUL|LSTBRF);
          break;
     case 'F':
          efvda->flags|=LSTFUL;
          break;
     default:
          errhlp(c == '?' ? LTPHLP : CNOTIL,usrptr->substt);
          return;
     }
     if (usrscn[usrnum] == NULL) {
          gostt(STRTLST);
     }
     else {
          prfmsg(LISTNG,getfnm(efvda->prvlst[0].fid));
          stmsglst();
     }
}

VOID
sstartl(VOID)                      /* state: what msg to start listing at  */
{
     switch (morcnc()) {
     case '.':
          cncchr();
          msgctx(efwork,firstnew(usaptr->userid,qsptr->curfor));
          break;
     case 'F':
          cncchr();
          msgctx(efwork,FIRSTM);
          break;
     case 'L':
          cncchr();
          msgctx(efwork,LASTM);
          break;
     default:
          if (isdigit(morcnc())) {
               msgctx(efwork,cnclon());
          }
          else {
               errhlp(morcnc() == '?' ? RTLHLP : FORHUH,usrptr->substt);
               return;
          }
          break;
     }
     stmsglst();
}

VOID
stmsglst(VOID)                     /* start listing message                */
{
     cncall();
     usrptr->substt=MSGLST;
     efvda->cflags&=~LSTDONE;
     prfmsg(LSTNHD);
     outprf(usrnum);
     clrprf();
     nxtmil();
}

VOID
nxtmil(VOID)                       /* get next message in list             */
{
     if (efvda->cflags&LSTDONE) {
          if (btuoba(usrnum) < OUTSIZ-LISTIL) {
               docyc(FALSE);
          }
          else {
               msglstd(FALSE);
               chkcyc();
          }
          return;
     }
     else {
          if (btuoba(usrnum) < MAXLSI) {
               docyc(FALSE);
               return;
          }
     }
     switch (nextmsg(efwork,msg,msgtxt)) {
     case GMEAGAIN:
          docyc(FALSE);
          return;
     case GMEOK:
          if (usrscn[usrnum] != NULL) {
               efvda->prvlst[0].fid=msg->forum;
          }
          if (efvda->flags&LSTFUL) {
               sumams(SUMLST,msg);
               outprf(usrnum);
               xmtext(lstxtd);
          }
          else if (efvda->flags&LSTBRF) {
               prfmsg(BRFLST,
                      spr(spr("%%%dld",strlen(l2as(sv.msgtot))),msg->msgid),
                      ncedat(msg->crdate),msg->topic,msg->from);
               if (noalen(prfptr) > 79) {
                    trcans(prfptr,79);
                    prfptr=&prfbuf[strlen(prfbuf)];
               }
               outprf(usrnum);
               clrprf();
               docyc(FALSE);
          }
          else {
               sumams(SUMLST,msg);
               outprf(usrnum);
               clrprf();
               docyc(FALSE);
          }
          break;
     default:
          efvda->cflags|=LSTDONE;
          if (btuoba(usrnum) < OUTSIZ-LISTIL) {
               docyc(FALSE);
          }
          else {
               msglstd(FALSE);
               chkcyc();
          }
          break;
     }
}

VOID
lstxtd(                            /* done xmting msg text while listing   */
GBOOL aborted)                     /*   TRUE if user aborted output        */
{
     if (aborted) {
          msglstd(aborted);
          chkcyc();
     }
     else {
          markoff(lstmrkd);
     }
}

VOID
lstmrkd(                           /* done marking listed message read     */
SHORT rc)                            /*   result code from GME               */
{
     switch (rc) {
     case GMEOK:
          usrptr->substt=MSGLST;
          docyc(FALSE);
          break;
     default:
          prfmsg(MARKERR,rc);
          outprf(usrnum);
          msglstd(FALSE);
          chkcyc();
          break;
     }
}

VOID
msglstd(                           /* message list done                    */
GBOOL aborted)                     /*   user aborted list                  */
{
     prfmsg(aborted ? ABOLST : ENDOFL);
     outprf(usrnum);
     clsgmerq(efwork);
     retmain();
}

VOID
trcans(                            /* truncate a stg to x NON-ANSI bytes   */
CHAR *stg,                         /*   string to truncate                 */
SHORT len)                         /*   # of NON-ANSI bytes to trunc to    */
{
     SHORT i;
     GBOOL inans;

     i=0;
     inans=FALSE;
     while (*stg != '\0') {
          if (inans && isalpha(*stg)) {
               inans=FALSE;
          }
          else if (*stg == 27) {
               inans=TRUE;
          }
          else if (!inans && i++ == (len-2)) {
               *stg++='*';
               *stg++='\r';
               *stg='\0';
               return;
          }
          stg++;
     }
}

VOID
sqscfgm(VOID)                      /* state: configure quickscan menu      */
{
     switch (cncchr()) {
     case '?':
          errhlp(QSCHLP,usrptr->substt);
          break;
     case 'C':
          clrqsc();
          gostt(usrptr->substt);
          break;
     case 'V':
          vwqsf();
          gostt(usrptr->substt);
          break;
     case '+':
          gostt(QCADD);
          break;
     case '-':
          gostt(QCDEL);
          break;
     case 'K':
          gostt(QSKWDS);
          break;
     case 'N':
          gostt(QSNMSG);
          break;
     case 'A':
          gostt(QSATONL);
          break;
     case 'T':
          gostt(QS2UONL);
          break;
     case 'F':
          gostt(QSFUONL);
          break;
     case 'M':
          gostt(QSSMSG);
          break;
     default:
          errhlp(CNOTIL,usrptr->substt);
          break;
     }
}

VOID
clrqsc(VOID)                       /* clear quickscan configuration        */
{
     SHORT i;

     qsptr->flags&=~(NEWMSG+WATONL+TOMEONL+FRMEONL);
     qsptr->stmsg=0L;
     qsptr->kwds[0]='\0';
     for (i=0 ; i < qsptr->nforums ; i++) {
          sfinqs(qsptr,igetfid(qsptr,i),FALSE);
     }
}

VOID
vwqsf(VOID)                        /* view forums in quickscan             */
{
     SHORT i,ctr;
     USHORT tmpfid;
     struct otscan *tmpots;

     prfmsg(QUICKC);
     tmpots=(struct otscan *)vdatmp;
     qsc2ots(qsptr,tmpots);
     for (ctr=0,i=0 ; i < tmpots->nforums ; ++i) {
          tmpfid=tmpots->forlst[i];
          if (faccok(tmpfid)) {
               prfmsg(QSFLST,getfnm(tmpfid));
               if ((ctr&3) == 3) {
                    prf("\r");
               }
               ++ctr;
          }
     }
     if ((ctr&3) != 0) {
          prf("\r");
     }
}

VOID
sqsadf(VOID)                       /* state: what forum to add to quickscan*/
{
     GBOOL markoff;
     CHAR *cp,*fornam,fname[FNMSIZ];
     USHORT forum;
     const struct fordef *fdp;

     switch (morcnc()) {
     case '?':
          listfor(usrptr->substt);
          break;
     case '/':
          cncchr();
     default:
          fornam=cncwrd();
          if (sameas(fornam,"all")) {
               cp=nxtcmd;
               morcnc();
               markoff=sameas(cncwrd(),"+all");
               if (!markoff) {
                    nxtcmd=cp;
               }
               *fname='\0';
               while ((fdp=nxtdefp(fname)) != NULL) {
                    if (faccok(fdp->forum)) {
                         if (!sfinqs(qsptr,fdp->forum,TRUE)) {
                              prfmsg(QSLFUL);
                              break;
                         }
                         else if (markoff) {
                              sethi(qsptr,fdp->forum,himsgid());
                         }
                    }
                    stlcpy(fname,fdp->name,FNMSIZ);
               };
               gostt(QSCFGM);
          }
          else {
               forum=getfid(fornam);
               if (forum != EMLID && faccok(forum)) {
                    if (gethi(qsptr,forum) > 0) {
                         sethi(qsptr,forum,himsgid());
                    }
                    else if (!sfinqs(qsptr,forum,TRUE)) {
                         prfmsg(QSLFUL);
                    }
                    gostt(QSCFGM);
               }
               else {
                    prfmsg(FNXST);
                    gostt(usrptr->substt);
               }
          }
     }
}

VOID
sqsrmf(VOID)                       /* state: remove a forum from quickscan */
{
     CHAR *fornam;
     SHORT i;
     USHORT forum;

     switch (morcnc()) {
     case '?':
          cncall();
          vwqsf();
          gostt(usrptr->substt);
          break;
     case '/':
          cncchr();
     default:
          fornam=cncwrd();
          if (sameas(fornam,"all")) {
               for (i=0 ; i < qsptr->nforums ; i++) {
                    sfinqs(qsptr,igetfid(qsptr,i),FALSE);
               }
               gostt(QSCFGM);
          }
          else {
               if ((forum=getfid(fornam)) != EMLID) {
                    sfinqs(qsptr,forum,FALSE);
                    gostt(QSCFGM);
               }
               else {
                    prfmsg(QCDERR);
                    gostt(usrptr->substt);
               }
          }
     }
}

VOID
sqsnmsg(VOID)                      /* state: include new msgs only in qsc? */
{
     switch (cncyesno()) {
     case 'Y':
          qsptr->flags|=NEWMSG;
          gostt(QSCFGM);
          break;
     case 'N':
          qsptr->flags&=~NEWMSG;
          gostt(QSCFGM);
          break;
     case '?':
          errhlp(QSNMSGH,usrptr->substt);
          break;
     default:
          errhlp(YORN,usrptr->substt);
          break;
     }
}

VOID
sqsatonl(VOID)                     /* state: include msg w/atts only in qsc*/
{
     switch (cncyesno()) {
     case 'Y':
          qsptr->flags|=WATONL;
          gostt(QSCFGM);
          break;
     case 'N':
          qsptr->flags&=~WATONL;
          gostt(QSCFGM);
          break;
     case '?':
          errhlp(QSATONH,usrptr->substt);
          break;
     default:
          errhlp(YORN,usrptr->substt);
          break;
     }
}

VOID
sqs2uonl(VOID)                     /* state: include msgs to only in qsc   */
{
     switch (cncyesno()) {
     case 'Y':
          qsptr->flags|=TOMEONL;
          gostt(QSCFGM);
          break;
     case 'N':
          qsptr->flags&=~TOMEONL;
          gostt(QSCFGM);
          break;
     case '?':
          errhlp(QS2UONH,usrptr->substt);
          break;
     default:
          errhlp(YORN,usrptr->substt);
          break;
     }
}

VOID
sqsfuonl(VOID)                     /* state: include only msgs from in qsc */
{
     switch (cncyesno()) {
     case 'Y':
          qsptr->flags|=FRMEONL;
          gostt(QSCFGM);
          break;
     case 'N':
          qsptr->flags&=~FRMEONL;
          gostt(QSCFGM);
          break;
     case '?':
          errhlp(QSFUONH,usrptr->substt);
          break;
     default:
          errhlp(YORN,usrptr->substt);
          break;
     }
}

VOID
sqssmsg(VOID)                      /* state: start quickscan @ what msg?   */
{
     if (morcnc() == '?') {
          errhlp(QSSMSGH,usrptr->substt);
     }
     else if (isdigit(morcnc())) {
          qsptr->stmsg=cnclon();
          gostt(QSCFGM);
     }
     else {
          errhlp(FORHUH,usrptr->substt);
     }
}

VOID
sqskwds(VOID)                      /* state: enter quickscan keyworks      */
{
     CHAR *inp;

     inp=cncall();
     if (sameas(inp,"?")) {
          errhlp(QCKWDH,usrptr->substt);
     }
     else {
          stlcpy(qsptr->kwds,inp,MAXSKWD);
          gostt(QSCFGM);
     }
}

VOID
startscn(VOID)                     /* start reading messages from scan     */
{
     struct otscan *ots;

     ots=usrscn[usrnum];
     clrprv();
     efvda->prvlst[0].fid=fstscnf(usaptr->userid,ots);
     if (efvda->prvlst[0].fid == EMLID) {
          prfmsg(NOQSF);
          outprf(usrnum);
          retmain();
          return;
     }
     prfmsg(SCANNG,getfnm(efvda->prvlst[0].fid));
     outprf(usrnum);
     inigmerq(efwork);
     setgmecb(efwork,scancbk);
     setscan(efwork,ots);
     inormrd(efwork,usaptr->userid,efvda->prvlst[0].fid,
             fstscnm(usaptr->userid,ots,efvda->prvlst[0].fid));
     seqctx(efwork,FSQSCN);
     efvda->curmid=0L;
     getnext(nxtscn);
}

VOID
clrprv(VOID)                       /* clear "previous position" list       */
{
     efvda->prvidx=0;
     setmem(efvda->prvlst,sizeof(struct formid)*MAXPRV,0);
}

VOID
scancbk(                           /* show progress of search/quickscan    */
INT evt,                           /*   current event                      */
INT rc)                            /*   result of last operation           */
{
     (VOID)rc;
     switch (evt) {
     case EVTNEWF:
          cycall();
          efvda->prvlst[0].fid=getfid(gmexinf());
          prfmsg(SCANNG,gmexinf());
          outprf(usrnum);
          clrprf();
          break;
     case EVTNEWM:
          if (srchipg()) {
               prf(".");
               outprf(usrnum);
               clrprf();
          }
          break;
     }
}

VOID
listcbk(                           /* show progress of quickscan list      */
INT evt,                           /*   current event                      */
INT rc)                            /*   result of last operation           */
{
     (VOID)rc;
     if (evt == EVTNEWF) {
          efvda->prvlst[0].fid=getfid(gmexinf());
          prfmsg(LISTNG,gmexinf());
          outprf(usrnum);
          clrprf();
     }
}

VOID
swhatfor(VOID)                     /* state: what forum to switch to       */
{
     USHORT tmpfid;

     switch (morcnc()) {
     case '?':
          listfor(usrptr->substt);
          break;
     case '/':
          cncchr();
     default:
          tmpfid=getfid(cncwrd());
          if (tmpfid != EMLID && foracc(tmpfid) > NOAXES) {
               joinfor(tmpfid);
               if (!morcnc()) {
                    condex();
               }
               gostt(FMAINM);
          }
          else {
               errhlp(FNXST,usrptr->substt);
          }
          break;
     }
}

static VOID
smodwht(VOID)                      /* state: what message to modify?       */
{
     if (isdigit(morcnc())) {
          inigmerq(efwork);
          efvda->tmpmid=cnclon();
          cncall();
          if (efvda->tmpmid > 0L) {
               inormrd(efwork,usaptr->userid,qsptr->curfor,efvda->tmpmid);
               if (chkcfl(efwork)) {
                    clsgmerq(efwork);
                    prfmsg(USING);
                    outprf(usrnum);
                    retmain();
               }
               else {
                    reget(get4mod);
               }
          }
          else {
               clsgmerq(efwork);
               prfmsg(NSUCHM);
               outprf(usrnum);
               retmain();
          }
     }
     else {
          errhlp(FORHUH,usrptr->substt);
     }
}

static VOID
get4mod(                           /* main-menu get message to modify      */
SHORT rc)                          /*   GME result code                    */
{
     switch (rc) {
     case GMEOK:
          if ((msg->flags&ISMPTR) && !(msg->flags&ISTCPY)) {
               prfmsg(NMODHDR);
          }
          else {
               efvda->curmid=msg->msgid;
               startmod(moddone);
               return;
          }
          break;
     case GMENFND:
          prfmsg(NSUCHM);
          break;
     default:
          prfmsg(READERR,rc);
          break;
     }
     outprf(usrnum);
     clsgmerq(efwork);
     retmain();
}

static VOID
moddone(                           /* main-menu modify finished            */
SHORT rc)                          /*   GME result code                    */
{
     switch (rc) {
     case GMEAGAIN:                /* user aborted edit                    */
          prf("");
          break;
     case GMEOK:
          prfmsg(SENTOK,l2as(efvda->curmid));
          break;
     case GMEUSE:
          prfmsg(USING);
          break;
     default:
          prfmsg(MODERR,rc);
          break;
     }
     outprf(usrnum);
     clsgmerq(efwork);
     retmain();
}

static VOID
sdelmsn(VOID)                      /* state: what message to delete?       */
{
     if (isdigit(morcnc())) {
          inigmerq(efwork);
          efvda->curmid=cnclon();
          cncall();
          if (efvda->curmid > 0L) {
               inormrd(efwork,usaptr->userid,qsptr->curfor,efvda->curmid);
               deletem(deldone);
          }
          else {
               clsgmerq(efwork);
               prfmsg(NSUCHM);
               outprf(usrnum);
               retmain();
          }
     }
     else {
          errhlp(FORHUH,usrptr->substt);
     }
}

static VOID
deldone(                           /* main-menu delete finished            */
SHORT rc)                          /*   GME result code                    */
{
     switch (rc) {
     case GMEOK:
          prfmsg(OKGONE,l2as(efvda->curmid));
          break;
     case GMENFND:
          prfmsg(NSUCHM);
          break;
     case GMEUSE:
          prfmsg(USING);
          break;
     default:
          prfmsg(DELERR,rc);
          break;
     }
     outprf(usrnum);
     clsgmerq(efwork);
     retmain();
}

VOID
nunapv(                            /* got next msg to check for !FILAPV    */
SHORT rc)                          /*   read result code                   */
{
     if (rc == GMEOK) {
          if (msg->flags&FILATT && !(msg->flags&FILAPV)) {
               efvda->cflags|=UATFND;
               prvscn(rc);
          }
          else {
               prf(".");
               outprf(usrnum);
               clrprf();
               usrptr->substt=NAPVING;
               docyc(FALSE);
          }
     }
     else {
          prvscn(rc);
     }
}

VOID
punapv(                            /* got prev msg to check for !FILAPV    */
SHORT rc)                          /*   read result code                   */
{
     if (rc == GMEOK) {
          if (msg->flags&FILATT && !(msg->flags&FILAPV)) {
               efvda->cflags|=UATFND;
               nxtscn(rc);
          }
          else {
               prf(".");
               outprf(usrnum);
               clrprf();
               usrptr->substt=PAPVING;
               docyc(FALSE);
          }
     }
     else if (efvda->cflags&UATFND) {
          nxtscn(rc);
     }
     else {
          cycall();
          prfmsg(EOSCAN);
          outprf(usrnum);
          clsgmerq(efwork);
          retmain();
     }
}

VOID
sfopmnu(VOID)                      /* state: Forum-Op menu                 */
{
     const struct fordef *tmpdef;

     switch (cncchr()) {
     case 'E':
          if (fopmfd) {
               gedtform();
          }
          else {
               tmpdef=curfdef();
               efvda->u.a.dfnpv=tmpdef->dfnpv;
               efvda->u.a.dfprv=tmpdef->dfprv;
               efvda->u.a.mxnpv=tmpdef->mxnpv;
               stlcpy(efvda->u.a.forlok,tmpdef->forlok,KEYSIZ);
               gostt(CFGACCM);
          }
          break;
     case 'S':
          gostt(UID2SET);
          break;
     case '?':
          cncall();
          if (usrptr->substt == FOPMNU) {
               prfmsg(FOPMHLP);
          }
          gostt(FOPMNU);
          break;
     default:
          errhlp(CNOTIL,usrptr->substt);
          break;
     }
}

VOID
soprmnu(VOID)                      /* state: operations menu               */
{
     switch (cncchr()) {
     case 'C':
          newforum();
          break;
     case 'E':
          gedtform();
          break;
     case 'D':
          gostt(FOR2DEL);
          break;
     case 'S':
          gostt(UID2SET);
          break;
     case 'U':
          gostt(UCPYSRC);
          break;
     case '?':
          cncall();
          if (usrptr->substt == OPRMNU) {
               prfmsg(OPMHLP);
          }
          gostt(OPRMNU);
          break;
     default:
          errhlp(CNOTIL,usrptr->substt);
          break;
     }
}

VOID
newforum(VOID)                     /* start up new forum creation process  */
{
     cncall();
     if (alcfsdc()) {
          inigmerq(efwork);
          usrptr->substt=CINIFOR;
          cinifor();
     }
     else {
          outprf(usrnum);
          condex();
          gostt(OPRMNS);
     }
}

GBOOL
alcfsdc(VOID)                      /* allocate FSD buffers for creating    */
{
     USHORT tmpsiz;

     efvda->cmtspc=fsdroom(CRFSDA,crtfsp,0);
     tmpsiz=fsdroom(CRFSDN,crtfsp,0);
     efvda->cmtspc=max(tmpsiz,efvda->cmtspc);
     efvda->fsdspc=malloc(efvda->cmtspc);
     if (efvda->fsdspc == NULL) {
          prfmsg(NOMEM);
          return(FALSE);
     }
     efvda->fsdtmp=malloc(sizeof(crtfmt)+CRTMPSZ);
     if (efvda->fsdtmp == NULL) {
          free(efvda->fsdspc);
          prfmsg(NOMEM);
          return(FALSE);
     }
     return(TRUE);
}

VOID
cinifor(VOID)                      /* cycled forum definition initializer  */
{
     switch (inifdef(efwork,fdsk)) {
     case GMEAGAIN:
          docyc(TRUE);
          return;
     case GMEOK:
          efvda->count=fdsk->necho;
          *descptr(efvda->count)='\0';
          fsdcrt();
          break;
     default:
          prfmsg(LOSTACC);
          outprf(usrnum);
          retmain();
          break;
     }
     cycall();
     chkcyc();
}

VOID
fsdcrt(VOID)                       /* start up FSD to create forum         */
{
     if (usaptr->ansifl&ANSON && usaptr->scnfse >= 23
                              && usaptr->scnwid >= 80) {
          fcrtprp(CRFSDA,1);
          fsdrhd("Creating Forum");
          fsdbkg(fsdrft());
          fsdego(fcrtvfy,fcrtdun);
          peruflg[usrnum]|=INFSD;
     }
     else {
          prf("\14");
          outprf(usrnum);
          clrprf();
          usrptr->substt=CRTFOR;
          btuoes(usrnum,TRUE);
     }
}

VOID
fcrtprp(                           /* Prepare for forum creation (FSD)     */
INT tmplt,                         /*   Answer template (from level 99)    */
INT emode)                         /*   Edit mode: 1=full 0=line -1=display*/
{
     CHAR dfnpv[20],
          dfprv[20],
          mxnpv[20],
          chgmsg[CHGSIZ],
          chgrdm[CHGSIZ],
          chgatt[CHGSIZ],
          chgadl[CHGSIZ],
          chgupk[CHGSIZ],
          chgdpk[CHGSIZ],
          ccr[CHGSIZ],
          msglif[CHGSIZ];

     stlcpy(dfnpv,accstr(fdsk->dfnpv),sizeof(dfnpv));
     stlcpy(dfprv,accstr(fdsk->dfprv),sizeof(dfprv));
     stlcpy(mxnpv,accstr(fdsk->mxnpv),sizeof(mxnpv));
     itoa(fdsk->chgmsg,chgmsg,10);
     itoa(fdsk->chgrdm,chgrdm,10);
     itoa(fdsk->chgatt,chgatt,10);
     itoa(fdsk->chgadl,chgadl,10);
     itoa(fdsk->chgupk,chgupk,10);
     itoa(fdsk->chgdpk,chgdpk,10);
     itoa(fdsk->ccr,ccr,10);
     itoa(fdsk->msglif,msglif,10);
     fsdroom(tmplt,crtfsp,emode);
     sprintf(efvda->fsdtmp,crtfmt,fdsk->name,'\0',
          fdsk->topic,'\0',
          fdsk->forop,'\0',
          fdsk->datfil,'\0',
          fdsk->attpath,'\0',
          fdsk->forlok,'\0',
          dfnpv,'\0',
          dfprv,'\0',
          mxnpv,'\0',
          chgmsg,'\0',
          chgrdm,'\0',
          chgatt,'\0',
          chgadl,'\0',
          chgupk,'\0',
          chgdpk,'\0',
          ccr,'\0',
          msglif,'\0',
          pflstr(fdsk->pfnlvl),'\0');
     fsdapr(efvda->fsdspc,efvda->cmtspc,efvda->fsdtmp);
}

INT
fcrtvfy(                           /* Create forum verify routine (FSD)    */
INT fldnum,                        /*   Field number                       */
CHAR *answer)                      /*   Proposed answer                    */
{
     CHAR tmpstr[GCMAXPTH],fptmp[GCMAXPTH];

     switch (fldnum) {
     case CNAME:
          if (*answer == '\0') {
               strcpy(fsdemg,"Forum name cannot be blank!");
               return(VFYREJ);
          }
          else if (fnmxst(answer)) {
               sprintf(fsdemg,"Forum \"%s\" already exists!",answer);
               return(VFYREJ);
          }
          else if (!valfornm(answer)) {
               sprintf(fsdemg,"\"%s\" is not a valid Forum name!",answer);
               return(VFYREJ);
          }
          break;
     case CFOROP:
          if (!uidxst(answer)) {
               sprintf(fsdemg,"User-ID \"%s\" not found!",answer);
               return(VFYREJ);
          }
          break;
     case CDATFIL:
          normspec(tmpstr,answer);
          if (!dexist(zapbck(fileparts(GCPART_PATH,tmpstr,fptmp,GCMAXPTH)))
           || !valfnm(fileparts(GCPART_FNAM,tmpstr,fptmp,GCMAXFNM))) {
               strcpy(fsdemg,"Invalid path or file name!");
               return(VFYREJ);
          }
          if (!valfdfnam(tmpstr)) {
               strcpy(fsdemg,"Data file name must use \".dat\" extension!");
               return(VFYREJ);
          }
          break;
     case CFORLOK:
          if (*answer != '\0' && !keynam(answer)) {
               sprintf(fsdemg,"\"%s\" is not a valid key name!",strupr(answer));
               return(VFYREJ);
          }
          break;
     }
     return(vfyadn(fldnum,answer));
}

VOID
fcrtdun(                           /* Forum create when-done routine (FSD) */
GBOOL save)                        /*   FALSE if user aborted edit         */
{
     if (infsdhup) {
          return;
     }
     setmeup();
     if (save) {
          stlcpy(fdsk->name,fsdnan(CNAME),FORNSZ);
          stlcpy(fdsk->topic,fsdnan(CTOPIC),TPCSIZ);
          stlcpy(fdsk->forop,fsdnan(CFOROP),UIDSIZ);
          fnmcse(stlcpy(fdsk->datfil,fsdnan(CDATFIL),MAXDFAP));
          fnmcse(stlcpy(fdsk->attpath,fsdnan(CATTPATH),GCMAXPTH));
          strupr(stlcpy(fdsk->forlok,fsdnan(CFORLOK),KEYSIZ));
          fdsk->dfnpv=2*fsdord(CDFNPV);
          fdsk->dfprv=2*fsdord(CDFPRV);
          fdsk->mxnpv=2*fsdord(CMXNPV);
          fdsk->chgmsg=atoi(fsdnan(CCHGMSG));
          fdsk->chgrdm=atoi(fsdnan(CCHGRDM));
          fdsk->chgatt=atoi(fsdnan(CCHGATT));
          fdsk->chgadl=atoi(fsdnan(CCHGADL));
          fdsk->chgupk=atoi(fsdnan(CCHGUPK));
          fdsk->chgdpk=atoi(fsdnan(CCHGDPK));
          fdsk->ccr=atoi(fsdnan(CCCR));
          fdsk->msglif=atoi(fsdnan(CMSGLIF));
          fdsk->pfnlvl=fsdord(CPFNLVL);
          if (*fdsk->name == '\0') {
               errhlp(NOBLFNM,FCRTNAM);
          }
          else {
               gostt(CRTFORM);
          }
     }
     usrptr->state=forstate;
     peruflg[usrnum]&=~INFSD;
     free(efvda->fsdspc);
     free(efvda->fsdtmp);
     if (!save) {
          prfmsg(FCRTABT);
          outprf(usrnum);
          condex();
          gostt(OPRMNS);
     }
     efvda->dftchr=getdft();
}

VOID
sfcrtnam(VOID)                     /* state: must specify name for forum   */
{
     CHAR *cp;

     cp=cncwrd();
     if (fnmxst(cp)) {
          prfmsg(FNMXST,cp);
          gostt(usrptr->substt);
     }
     else if (!valfornm(cp)) {
          prfmsg(INVFNM,cp);
          gostt(usrptr->substt);
     }
     else {
          stlcpy(fdsk->name,cp,FORNSZ);
          gostt(CRTFORM);
     }
}

VOID
scrtform(VOID)                     /* state: create forum menu             */
{
     switch (cncchr()) {
     case 'D':
          cncall();
          if (alcfsdc()) {
               fsdcrt();
          }
          else {
               gostt(usrptr->substt);
          }
          break;
     case 'E':
          efvda->flags|=CRTING;
          listecho();
          gostt(EDECHO);
          break;
     case 'H':
          edfhlp(fdsk->topic);
          break;
     case '?':
          errhlp(CRTFHLP,CRTFORM);
          break;
     case '.':
          cncall();
          gcrtfor();
          break;
     default:
          errhlp(CNOTIL,usrptr->substt);
          break;
     }
}

VOID
gcrtfor(VOID)                      /* start cycled create-a-forum process  */
{
     fdsk->necho=efvda->count;
     inigmerq(efwork);
     usrptr->substt=CREATING;
     ccrtfor();
}

VOID
ccrtfor(VOID)                      /* cycled create-a-forum process        */
{
     SHORT rc;

     setmeup();
     switch (rc=creatfor(efwork,fdsk,descptr(efvda->count),(CHAR *)echo)) {
     case GMEAGAIN:
          docyc(TRUE);
          return;
     case GMEOK:
          faccnot(fdsk->forum,fdsk->forop);
          prfmsg(FCRTCNF,fdsk->name);
          joinfor(fdsk->forum);
          break;
     case GMEDUP:
          prfmsg(DUPFNM,fdsk->name);
          break;
     case GMEMEM:
          prfmsg(NMEMFOR);
          break;
     case GMENFID:
          prfmsg(NOFIDS);
          break;
     case GME2MFR:
          prfmsg(FOR2MNY);
          break;
     case GME2MFL:
          prfmsg(FIL2MNY);
          break;
     default:
          prfmsg(FCRTERR,rc);
          break;
     }
     outprf(usrnum);
     btulok(usrnum,FALSE);
     condex();
     gostt(OPRMNS);
     chkcyc();
}

VOID
gedtform(VOID)                     /* goto edit forum menu                 */
{
     const struct fordef *tmpdef;

     tmpdef=getdefp(qsptr->curfor);
     efvda->count=tmpdef->necho;
     getallf(qsptr->curfor,fdsk,descptr(tmpdef->necho),(CHAR *)echo);
     stlcpy(efvda->tmpadr,tmpdef->forop,UIDSIZ);
     stlcpy(efvda->tmppath,fdsk->datfil,GCMAXPTH);
     movmem(tmpdef,fdef,sizeof(struct fordef));
     gostt(EDTFMNU);
}

VOID
sedtform(VOID)                     /* state: edit forum menu               */
{
     switch (cncchr()) {
     case 'D':
          cncall();
          if (alcfsde()) {
               fsdedt();
          }
          else {
               gostt(usrptr->substt);
          }
          break;
     case 'E':
          efvda->flags&=~CRTING;
          listecho();
          gostt(EDECHO);
          break;
     case 'H':
          edfhlp(fdef->topic);
          break;
     case '?':
          errhlp(EDTFHLP,usrptr->substt);
          break;
     case '.':
          cncall();
          gedtfor();
          break;
     default:
          errhlp(CNOTIL,usrptr->substt);
          break;
     }
}

GBOOL
alcfsde(VOID)                      /* allocate FSD buffers for editing */
{
     UINT tmpsiz;

     edtfsp=curfacc() >= SYAXES ? sedtfsp : fedtfsp;
     efvda->cmtspc=fsdroom(FORDSP2,edtfsp,-1);
     tmpsiz=fsdroom(EDFSDA2,edtfsp,0);
     efvda->cmtspc=max(tmpsiz,efvda->cmtspc);
     tmpsiz=fsdroom(EDFSDN2,edtfsp,0);
     efvda->cmtspc=max(tmpsiz,efvda->cmtspc);
     efvda->fsdspc=malloc(efvda->cmtspc);
     if (efvda->fsdspc == NULL) {
          prfmsg(NOMEM);
          return(FALSE);
     }
     efvda->fsdtmp=malloc(sizeof(edtfmt)+EDTMPSZ);
     if (efvda->fsdtmp == NULL) {
          free(efvda->fsdspc);
          prfmsg(NOMEM);
          return(FALSE);
     }
     return(TRUE);
}

VOID
fsdedt(VOID)                       /* start editing forum                  */
{
     if (usaptr->ansifl&ANSON && usaptr->scnfse >= 23
                              && usaptr->scnwid >= 80) {
          fedtprp(EDFSDA2,1);
          fsdrhd("Editing Forum");
          fsdbkg(fsdrft());
          fsdscb->flddat[EDATE].flags|=FFFAVD;
          fsdscb->flddat[EDATFIL].flags|=FFFAVD;
          fsdscb->flddat[EATTPATH].flags|=FFFAVD;
          fsdego(fedtvfy,fedtdun);
          peruflg[usrnum]|=INFSD;
     }
     else {
          fedtprp(FORDSP2,-1);
          fsddsp(fsdrft());
          gostt(FSDCHG);
     }
}

VOID
sfsdchg(VOID)                      /* Non-ANSI want-to-edit prompt         */
{
     switch (cncyesno()) {
     case 'Y':
          cncall();
          usrptr->substt=EDTFOR;
          prf("\r");
          outprf(usrnum);
          btuoes(usrnum,TRUE);
          break;
     case 'N':
          free(efvda->fsdspc);
          free(efvda->fsdtmp);
          gostt(EDTFMNU);
          break;
     default:
          errhlp(YORN,FSDCHG);
          break;
     }
}

VOID
fedtprp(                           /* Prepare for forum editing (FSD)      */
INT tmplt,                         /*   Answer template (from level 99)    */
INT emode)                         /*   Edit mode: 1=full 0=line -1=display*/
{
     CHAR dfnpv[20],
          dfprv[20],
          mxnpv[20],
          chgmsg[CHGSIZ],
          chgrdm[CHGSIZ],
          chgatt[CHGSIZ],
          chgadl[CHGSIZ],
          chgupk[CHGSIZ],
          chgdpk[CHGSIZ],
          ccr[CHGSIZ],
          msglif[CHGSIZ],
          dtstr[sizeof("MM/DD/YYYY HH:MM:SS")];

     stlcpy(dfnpv,accstr(fdef->dfnpv),sizeof(dfnpv));
     stlcpy(dfprv,accstr(fdef->dfprv),sizeof(dfprv));
     stlcpy(mxnpv,accstr(fdef->mxnpv),sizeof(mxnpv));
     itoa(fdef->chgmsg,chgmsg,10);
     itoa(fdef->chgrdm,chgrdm,10);
     itoa(fdef->chgatt,chgatt,10);
     itoa(fdef->chgadl,chgadl,10);
     itoa(fdef->chgupk,chgupk,10);
     itoa(fdef->chgdpk,chgdpk,10);
     itoa(fdef->ccr,ccr,10);
     itoa(fdef->msglif,msglif,10);
     stlcpy(dtstr,ncdatel(fdef->crdate),sizeof(dtstr));
     stlcat(dtstr," ",sizeof(dtstr));
     stlcat(dtstr,nctime(fdef->crtime),sizeof(dtstr));
     fsdroom(tmplt,edtfsp,emode);
     sprintf(efvda->fsdtmp,edtfmt,fdef->name,'\0',
          dtstr,'\0',
          fdef->topic,'\0',
          fdef->forop,'\0',
          efvda->tmppath,'\0',
          fdef->attpath,'\0',
          fdef->forlok,'\0',
          dfnpv,'\0',
          dfprv,'\0',
          mxnpv,'\0',
          chgmsg,'\0',
          chgrdm,'\0',
          chgatt,'\0',
          chgadl,'\0',
          chgupk,'\0',
          chgdpk,'\0',
          ccr,'\0',
          msglif,'\0',
          pflstr((SHORT)fdef->pfnlvl),'\0');
     fsdapr(efvda->fsdspc,efvda->cmtspc,efvda->fsdtmp);
}

VOID
fedtdun(                           /* Forum edit when-done routine (FSD)   */
GBOOL save)                        /*   FALSE if user aborted edit         */
{
     if (infsdhup) {
          return;
     }
     setmeup();
     if (save) {
          stlcpy(fdef->name,fsdnan(ENAME),FORNSZ);
          stlcpy(fdef->topic,fsdnan(ETOPIC),TPCSIZ);
          stlcpy(fdef->forop,fsdnan(EFOROP),UIDSIZ);
          strupr(stlcpy(fdef->forlok,fsdnan(EFORLOK),KEYSIZ));
          fdef->dfnpv=2*fsdord(EDFNPV);
          fdef->dfprv=2*fsdord(EDFPRV);
          fdef->mxnpv=2*fsdord(EMXNPV);
          fdef->chgmsg=atoi(fsdnan(ECHGMSG));
          fdef->chgrdm=atoi(fsdnan(ECHGRDM));
          fdef->chgatt=atoi(fsdnan(ECHGATT));
          fdef->chgadl=atoi(fsdnan(ECHGADL));
          fdef->chgupk=atoi(fsdnan(ECHGUPK));
          fdef->chgdpk=atoi(fsdnan(ECHGDPK));
          fdef->ccr=atoi(fsdnan(ECCR));
          fdef->msglif=atoi(fsdnan(EMSGLIF));
          fdef->pfnlvl=fsdord(EPFNLVL);
     }
     else {
          prfmsg(FDEDABT);
     }
     usrptr->state=forstate;
     peruflg[usrnum]&=~INFSD;
     free(efvda->fsdspc);
     free(efvda->fsdtmp);
     gostt(EDTFMNU);
     efvda->dftchr=getdft();
}

INT
fedtvfy(                           /* Edit forum verify routine (FSD)      */
INT fldnum,                        /*   Field number                       */
CHAR *answer)                      /*   Proposed answer                    */
{
     setmeup();
     switch (fldnum) {
     case ENAME:
          if (*answer == '\0') {
               strcpy(fsdemg,"Forum name cannot be blank!");
               return(VFYREJ);
          }
          else if (fnmxst(answer) && getfid(answer) != fdef->forum) {
               sprintf(fsdemg,"Forum \"%s\" already exists!",answer);
               return(VFYREJ);
          }
          else if (!valfornm(answer)) {
               sprintf(fsdemg,"\"%s\" is not a valid Forum name!",answer);
               return(VFYREJ);
          }
          break;
     case EFOROP:
          if (!uidxst(answer)) {
               sprintf(fsdemg,"User-ID \"%s\" not found!",answer);
               return(VFYREJ);
          }
          break;
     case EFORLOK:
          if (*answer != '\0' && !keynam(answer)) {
               sprintf(fsdemg,"\"%s\" is not a valid key name!",strupr(answer));
               return(VFYREJ);
          }
          break;
     }
     return(vfyadn(fldnum,answer));
}

VOID
gedtfor(VOID)                      /* start cycled edit-a-forum process    */
{
     inigmerq(efwork);
     if (gencfl(efwork,fdef->forum,0L)) {
          clsgmerq(efwork);
          errhlp(EDFCFL,usrptr->substt);
     }
     else {
          fdef->necho=efvda->count;
          if (!uidxst(fdef->forop)) {
               strcpy(fdef->forop,"Sysop");
          }
          usrptr->substt=EDITING;
          cedtfor();
     }
}

VOID
cedtfor(VOID)                      /* cycled edit-a-forum process          */
{
     SHORT rc;

     setmeup();
     switch (rc=modfor(efwork,fdef,descptr(efvda->count),(CHAR *)echo)) {
     case GMEAGAIN:
          docyc(TRUE);
          return;
     case GMEOK:
          if (!sameas(efvda->tmpadr,fdef->forop)) {
               faccnot(fdef->forum,efvda->tmpadr);
               faccnot(fdef->forum,fdef->forop);
          }
          prfmsg(FEDTCNF,fdef->name);
          break;
     case GMEDUP:
          prfmsg(DUPFNM,fdef->name);
          break;
     default:
          prfmsg(FEDTERR,rc);
          break;
     }
     outprf(usrnum);
     btulok(usrnum,FALSE);
     condex();
     gostt(curfacc() >= SYAXES ? OPRMNS : FOPMNS);
     chkcyc();
}

VOID
sedecho(VOID)                      /* state: edit echoes prompt            */
{
     switch (cncchr()) {
     case 'A':
          if (strlen(descptr(efvda->count))+1+efvda->count*MAXADR
            > MAXFDV-MAXADR) {
               errhlp(ECHOMEM,usrptr->substt);
          }
          else {
               gostt(ECH2ADD);
          }
          break;
     case 'D':
          if (efvda->count == 0) {
               errhlp(NOECH2D,usrptr->substt);
          }
          else {
               gostt(ECH2DEL);
          }
          break;
     case '?':
          cncall();
          listecho();
          gostt(usrptr->substt);
          break;
     default:
          errhlp(CNOTIL,usrptr->substt);
          break;
     }
}

VOID
listecho(VOID)                     /* list echoes while creating/editing   */
{
     INT i;

     if (efvda->count == 0) {
          prfmsg(NOECHOS);
     }
     else {
          prfmsg(ECHOES);
          for (i=0 ; i < efvda->count ; ++i) {
               prfmsg(ECHOLST,i+1,echo[i]);
          }
     }
}

VOID
sech2add(VOID)                     /* state: echo to add to list           */
{
     CHAR tmpadr[MAXADR];

     stlcpy(tmpadr,cncall(),MAXADR);
     if (isexpa(tmpadr) && fixadr(NULL,tmpadr)) {
          descptr(efvda->count);
          movmem(desc,desc+MAXADR,strlen(desc)+1);
          stlcpy(echo[efvda->count++],tmpadr,MAXADR);
          gostt(EDECHO);
     }
     else {
          errhlp(INVECHO,usrptr->substt);
     }
}

VOID
sech2del(VOID)                     /* state: echo to add to list           */
{
     INT echonum;

     echonum=cncint();
     if (echonum < 1 || echonum > efvda->count) {
          prfmsg(INVECHN,echonum,efvda->count);
          gostt(usrptr->substt);
          return;
     }
     descptr(efvda->count--);
     movmem(echo[echonum],echo[echonum-1],efvda->count*MAXADR+strlen(desc)+1);
     gostt(EDECHO);
}

VOID
edfhlp(                            /* edit forum help message              */
CHAR *topic)                       /*   pointer to forum topic buffer      */
{
     UINT descsiz;
     CHAR *tmpdesc;

     descsiz=MAXFDV-efvda->count*MAXADR;
     tmpdesc=malloc(descsiz);
     if (tmpdesc == NULL) {
          errhlp(NOMEM,usrptr->substt);
     }
     else {
          peruflg[usrnum]|=INEDHLP;
          efvda->savstt=usrptr->substt;
          efvda->ccptr=tmpdesc;
          /* stzcpy() to prevent possible FSE comprbuf() error */
          stzcpy(tmpdesc,descptr(efvda->count),descsiz);
          bgnedt(descsiz,tmpdesc,TPCSIZ,topic,efhdone,0);
     }
}

SHORT                              /*   return 1 to stay in module         */
efhdone(                           /* done editing forum help message      */
SHORT edflgs)                      /*   edit result flags                  */
{
     setmeup();
     peruflg[usrnum]&=~INEDHLP;
     usrptr->state=forstate;
     if (edflgs&ED_QUITEX) {
          prfmsg(EDHLPABT);
     }
     else {
          stlcpy(descptr(efvda->count),efvda->ccptr,MAXFDV-efvda->count*MAXADR);
     }
     free(efvda->ccptr);
     gostt(efvda->savstt);
     efvda->dftchr=getdft();
     outprf(usrnum);
     return(1);
}

VOID
sfor2del(VOID)                     /* state: selecting forum to delete     */
{
     CHAR *cp;
     USHORT tmpfid;

     switch (morcnc()) {
     case '?':
          listfor(usrptr->substt);
          break;
     case '/':
          cncchr();
     default:
          tmpfid=getfid(cp=cncwrd());
          if (tmpfid == EMLID) {
               prfmsg(DFNXST,cp);
               cncall();
               gostt(usrptr->substt);
          }
          else if (tmpfid == qsptr->curfor) {
               errhlp(CANTDC,usrptr->substt);
          }
          else if (tmpfid == dftfor) {
               errhlp(CANTDDF,usrptr->substt);
          }
          else {
               inigmerq(efwork);
               if (gencfl(efwork,tmpfid,0L)) {
                    clsgmerq(efwork);
                    errhlp(CANTDL,usrptr->substt);
               }
               else {
                    efvda->tmpfid=tmpfid;
                    getdefb(tmpfid,fdef);
                    stlcpy(efvda->tmpadr,fdef->name,FORNSZ);
                    gostt(CNFDEL);
               }
          }
          break;
     }
}

VOID
scnfdel(VOID)                      /* state: confirm forum to delete       */
{
     switch (cncyesno()) {
     case 'Y':
          gdelfor();
          break;
     case 'N':
          clsgmerq(efwork);
          condex();
          gostt(OPRMNS);
          break;
     default:
          errhlp(YORN,usrptr->substt);
          break;
     }
}

VOID
gdelfor(VOID)                      /* start cycled delete-a-forum process  */
{
     cncall();
     fdef->necho=efvda->count;
     usrptr->substt=FDELING;
     docyc(TRUE);
}

VOID
cdelfor(VOID)                      /* cycled delete-a-forum process        */
{
     SHORT rc;

     setmeup();
     switch (rc=delfor(efwork,efvda->tmpfid)) {
     case GMEAGAIN:
          docyc(TRUE);
          return;
     case GMEUSE:
          errhlp(SNUKIN,FOR2DEL);
          break;
     case GMEOK:
     default:
          if (rc == GMEOK) {
               prfmsg(FDELCNF,efvda->tmpadr);
          }
          else {
               prfmsg(FDELERR,rc);
          }
          outprf(usrnum);
          btulok(usrnum,FALSE);
          condex();
          gostt(OPRMNS);
          break;
     }
     chkcyc();
}

VOID
scfgaccm(VOID)                     /* state: edit forum access menu        */
{
     efvda->count=0;
     switch (cncchr()) {
     case 'N':
          ++efvda->count;
     case 'P':
          ++efvda->count;
     case 'M':
          ++efvda->count;
          gostt(SETDFAC);
          break;
     case 'K':
          gostt(MODLOC);
          break;
     case '?':
          errhlp(FCFGACH,usrptr->substt);
          break;
     default:
          errhlp(CNOTIL,usrptr->substt);
          break;
     }
}

VOID
ssetdfac(VOID)                     /* state: select a default access level */
{
     INT newlvl;

     switch (cncchr()) {
     case 'Z':
          newlvl=NOAXES;
          break;
     case 'R':
          newlvl=RDAXES;
          break;
     case 'D':
          newlvl=DLAXES;
          break;
     case 'W':
          newlvl=WRAXES;
          break;
     case 'U':
          newlvl=ULAXES;
          break;
     case 'C':
          newlvl=COAXES;
          break;
     default:
          errhlp(CNOTIL,usrptr->substt);
          return;
     }
     switch (efvda->count) {
     case 3:
          efvda->u.a.dfnpv=newlvl;
          break;
     case 2:
          efvda->u.a.dfprv=newlvl;
          break;
     case 1:
          efvda->u.a.mxnpv=newlvl;
          break;
     default:
          ASSERT(FALSE);
     }
     if (cfgfacc(qsptr->curfor,&efvda->u.a) == GMEOK) {
          gostt(CFGACCM);
     }
     else {
          prfmsg(LOSTACC);
          outprf(usrnum);
          retmain();
     }
}

VOID
smodloc(VOID)                      /* state: enter privileged access key   */
{
     CHAR tmpkey[KEYSIZ];

     stlcpy(tmpkey,cncwrd(),KEYSIZ);
     if (sameas(tmpkey,"?")) {
          prfmsg(MODLOCH,forprv);
          gostt(usrptr->substt);
          return;
     }
     if (sameas(tmpkey,".")) {
          stlcpy(tmpkey,forprv,KEYSIZ);
     }
     if (keynam(tmpkey)) {
          stlcpy(efvda->u.a.forlok,strupr(tmpkey),KEYSIZ);
          if (cfgfacc(qsptr->curfor,&efvda->u.a) == GMEOK) {
               gostt(CFGACCM);
          }
          else {
               prfmsg(LOSTACC);
               outprf(usrnum);
               retmain();
          }
     }
     else {
          errhlp(BADKYN,usrptr->substt);
     }
}

VOID
suid2set(VOID)                     /* state: get User-ID to set access for */
{
     CHAR tmpuid[UIDSIZ];

     stlcpy(tmpuid,cncall(),UIDSIZ);
     switch (hdluid(tmpuid)) {
     case UIDFND:
          stlcpy(tmpuid,uidxrf.userid,MAXADR);
          stlcpy(efvda->tmpadr,tmpuid,UIDSIZ);
          prfmsg(CURLVL,tmpuid,accstr(gforac(tmpuid,qsptr->curfor)));
          gostt(haskey(forsys) ? SYSUID : FORUID);
          break;
     case UIDPMT:
          gostt(usrptr->substt);
          return;
     case UIDCAL:
          return;
     default:
          ASSERT(FALSE);
     }
     clrxrf();
}

VOID
sforuid(VOID)                      /* state: setting specific user's access*/
{
     SHORT rc,newlvl,actlvl;
     CHAR oldop[UIDSIZ];

     switch (cncchr()) {
     case 'Z':
          newlvl=NOAXES;
          break;
     case 'R':
          newlvl=RDAXES;
          break;
     case 'D':
          newlvl=DLAXES;
          break;
     case 'W':
          newlvl=WRAXES;
          break;
     case 'U':
          newlvl=ULAXES;
          break;
     case 'C':
          newlvl=COAXES;
          break;
     case '*':
          newlvl=NOTSET;
          break;
     case 'F':
          if (curfacc() >= SYAXES) {
               newlvl=OPAXES;
               break;
          }
     default:
          errhlp(CNOTIL,usrptr->substt);
          return;
     case '?':
          gostt(curfacc() >= SYAXES ? SSETACH : FSETACH);
          return;
     }
     stlcpy(oldop,curfdef()->forop,UIDSIZ);
     switch (rc=setaxes(qsptr->curfor,efvda->tmpadr,newlvl)) {
     case GMEOK:
          if (!sameas(oldop,curfdef()->forop)
           && !sameas(oldop,efvda->tmpadr)) {
               faccnot(qsptr->curfor,oldop);
          }
          faccnot(qsptr->curfor,efvda->tmpadr);
          actlvl=gforac(efvda->tmpadr,qsptr->curfor);
          if (actlvl != newlvl) {
               if (newlvl == NOAXES && actlvl == RDAXES) {
                    prfmsg(DFTSOV);
               }
               else if (actlvl == SYAXES) {
                    prfmsg(RMNSYS2);
               }
               else if (newlvl == NOTSET) {
                    prfmsg(BAKDFT,efvda->tmpadr,accstr(actlvl));
               }
               else {
                    prfmsg(MXNLOVN,efvda->tmpadr,accstr(actlvl));
               }
          }
          break;
     case GMEMEM:
          prfmsg(NOACROOM);
          break;
     case GMEACC:
          prfmsg(LOSTACC);
          break;
     default:
          prfmsg(SETACERR,rc);
          break;
     }
     outprf(usrnum);
     condex();
     gostt(curfacc() >= SYAXES ? OPRMNS : FOPMNS);
}

VOID
sucpysrc(VOID)                /* state: user-copy source User-ID?          */
{
     stlcpy(efvda->tmpadr,cncall(),UIDSIZ);
     if (sameas(efvda->tmpadr,"?")) {
          errhlp(UCPYHLP,usrptr->substt);
     }
     else if (uidxst(efvda->tmpadr)) {
          gostt(UCPDST);
     }
     else {
          prfmsg(UIDNFND,efvda->tmpadr);
          gostt(usrptr->substt);
     }
}

VOID
sucpdst(VOID)                 /* state: user-copy destination User-ID?     */
{
     CHAR *cp;

     cp=cncall();
     switch (cpyaxes(cp,efvda->tmpadr)) {
     case GMEOK:
          gostt(UCPDST2);
          break;
     case GMENSRC:
          prfmsg(UIDNFND,efvda->tmpadr);
          outprf(usrnum);
          condex();
          gostt(OPRMNS);
          break;
     case GMENDST:
     default:
          prfmsg(UIDNFND,cp);
          gostt(usrptr->substt);
          break;
     }
}

GBOOL                              /*   returns TRUE if warning issued     */
fpfnck(VOID)                       /* forums profanity checker             */
{
     INT fpfnlvl;

     fpfnlvl=curfdef()->pfnlvl;
     if (fpfnlvl != DFTPFN && pfnlvl > fpfnlvl) {
          usrptr->pfnacc-=pfnlvl-fpfnlvl;
          pfnlvl=fpfnlvl;
     }
     return(pfnwrn());
}

static VOID
retmain(VOID)                      /* return to main menu                  */
{
     efvda->curmid=0L;
     efvda->prethr=0L;
     efvda->flags&=~THRING;
     efvda->cflags&=~SCN4UA;
     if (usrscn[usrnum] != NULL) {
          unrscan(usrnum);
          usrscn[usrnum]=NULL;
     }
     btulok(usrnum,FALSE);
     condex();
     gostt(FMAINS);
}

INT
curfacc(VOID)                      /* get current user's forum access level*/
{
     return(foracc(qsptr->curfor));
}

const struct fordef *
curfdef(VOID)                      /* get current forum definition struct  */
{
     return(getdefp(qsptr->curfor));
}

struct fordef *
getcfd(VOID)                       /* get current forum def into buffer    */
{
     return(getdefb(qsptr->curfor,fdef));
}

const CHAR *
curfnm(VOID)                       /* name of current Forum                */
{
     return(getfnm(qsptr->curfor));
}

const CHAR *
curftpc(VOID)                      /* topic of current Forum               */
{
     return(getftpc(qsptr->curfor));
}

GBOOL
dnldok(                            /* can user download attachment (if any)*/
USHORT forum)                      /*   forum in which message resides     */
{
     return((msg->flags&FILATT)
         && (foracc(forum) >= OPAXES
          || ((msg->flags&FILAPV) && foracc(forum) >= DLAXES)));
}

GBOOL
isauth(VOID)                       /* is current user author of current msg*/
{
     return(sameas(msg->from,usaptr->userid) && msg->crdate >= usaptr->credat);
}

GBOOL
fechoed(VOID)                      /* is current forum echoed?             */
{
     return(getdefp(qsptr->curfor)->necho > 0);
}

VOID
showhlp(VOID)                      /* show Forums help message             */
{
     prfmsg(FHLPHDR);
     outprf(usrnum);
     getdesc(qsptr->curfor,msgtxt);
     xmtext(shwhlp2);
}

VOID
shwhlp2(                           /* second part of Forum help message    */
SHORT aborted)                     /*   TRUE if user aborted output        */
{
     if (aborted) {
          retmain();
          chkcyc();
     }
     else {
          getdefb(qsptr->curfor,fdef);
          usrptr->substt=FINFO;
          cshowhlp();
     }
}

VOID
cshowhlp(VOID)                     /* cycled Forums help message outputter */
{
     if (btuoba(usrnum) < OUTSIZ-2) {
          docyc(FALSE);
          return;
     }
     prfmsg(FINFO,accstr(curfacc()),fdef->forop,
                  chcred(usrptr->crdrat),
                  chcred(fdef->chgmsg),
                  chcred(fdef->chgatt),
                  plperk(fdef->chgupk),
                  chcred(fdef->chgadl),
                  plperk(fdef->chgdpk),
                  fdef->chgatt > 0 ? "rebate" : "bonus");
     if (fdef->msglif >= 0) {
          prfmsg(INFFLM,fdef->msglif);
     }
     else {
          prfmsg(INFFUL);
     }
     prfmsg(FHLPTRL);
     outprf(usrnum);
     condex();
     gostt(FMAINM);
     chkcyc();
}

CHAR *
chcred(                            /* charge-amount phrase for info        */
INT amt)                           /*   amount in question                 */
{
     if (amt == 0) {
          return("not charged");
     }
     else if (amt > 0) {
          return(spr("charged %d credits",amt));
     }
     else {
          return(spr("given %d credits",-amt));
     }
}

CHAR *
plperk(                            /* plus-per-kilobyte phrase for info    */
INT amt)                           /*   amount in question                 */
{
     static CHAR fmtstr[]=" (plus %d credits %s per kilobyte)";
     static CHAR ctr=0;
     static CHAR buf[2][34-4+7+10+1];

     if (amt == 0) {
          return("");
     }
     else if (amt > 0) {
          sprintf(buf[(SHORT)(ctr^=1)],fmtstr,amt,"charged");
     }
     else {
          sprintf(buf[(SHORT)(ctr^=1)],fmtstr,-amt,"given");
     }
     return(buf[(SHORT)ctr]);
}

CHAR *                             /*   returns copy of pointer            */
descptr(                           /* set pointer to forum description     */
INT necho)                         /*   number of echoes                   */
{
     return(desc=echo[necho]);
}

CHAR *                             /*   returns copy of pointer            */
zapbck(                            /* remove trailing backslash if present */
CHAR *dir)                         /*   from this directory name           */
{
     UINT pos;

     if (dir[pos=strlen(dir)-1] == SL) {
          dir[pos]='\0';
     }
     return(dir);
}

CHAR *
pflstr(                            /* get string representing              */
SHORT pflvl)                       /*   profanity suppression level        */
{
     switch (pflvl) {
     case 0:
          return("NONE");
     case 1:
          return("MILD");
     case 2:
          return("MODERATE");
     case 3:
          return("SEVERE");
     case DFTPFN:
          return("DEFAULT");
     }
     ASSERT(FALSE);
     return("");
}

VOID
listfor(                           /* list forums                          */
INT retstt)                        /*   substate to reprompt with when done*/
{
     cncall();
     efvda->count=0;
     efvda->savstt=retstt;
     prfmsg(FLSTHDR);
     outprf(usrnum);
     startlst(flstr,flstd);
}

GBOOL
flstr(VOID)                        /* forum lister                         */
{
     SHORT counter;
     CHAR *nthrs,*nmsgs,*nfiles;
     const struct fordef *tmpdef;

     counter=0;
     do {
          tmpdef=seqdefp(efvda->count++);
          if (tmpdef == NULL) {
               return(FALSE);
          }
          if (faccok(tmpdef->forum)) {
               nthrs=ul2as(tmpdef->nthrs);
               nmsgs=ul2as(tmpdef->nmsgs);
               nfiles=ul2as(tmpdef->nfiles);
               sprintf(vdatmp,getmsg(FLSTLIN),tmpdef->name,nmsgs,nfiles,nthrs,
                       tmpdef->forop,tmpdef->topic);
               prf(vdatmp);
               outprf(usrnum);
          }
          clrprf();
     } while (++counter < 64 && btuoba(usrnum) > LISTIL);
     return(TRUE);
}

VOID
flstd(                             /* done listing forums                  */
SHORT rc)                          /*   (not used, for compatibility)      */
{
     (VOID)rc;
     prfmsg(FLSTLR);
     gostt(efvda->savstt);
}

VOID
faccnot(                           /* notify user of new forum access      */
USHORT forum,                      /*   forum ID to notify about           */
CHAR *userid)                      /*   User-ID to notify                  */
{
     SHORT acc;

     if (!sameas(userid,usaptr->userid) && onsys(userid)
      && !(usrptr->flags&INVISB)) {
          acc=gforac(userid,forum);
          if (acc == OPAXES) {
               prfmlt(UNOWOP,getfnm(forum));
          }
          else {
               prfmlt(UNEWAC,usaptr->userid,accstr(acc),getfnm(forum));
          }
          injoth();
          prfmsg(SNOTIFD,userid);
          outprf(usrnum);
          clrprf();
     }
}
