/***************************************************************************
 *                                                                         *
 *   SCRIPT.C                                                              *
 *                                                                         *
 *   Copyright (c) 1995-1997 Galacticomm, Inc.      All Rights Reserved.   *
 *                                                                         *
 *   This is the routine suite that supports Dial-Out-like scripting for   *
 *   online sessions.                                                      *
 *                                                                         *
 *                                                   - C. Robert  9/9/95   *
 *                                                   - R. Stein  1/26/96   *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "script.h"

#define FILREV "$Revision: 12 $"

static VOID clistb(struct scrinf *scrptr,UINT len,CHAR *bytes);
static VOID devstb(struct scrinf *scrptr,CHAR *stg);
static VOID admstb(struct scrinf *scrptr,CHAR *stg);
static INT dftpscr(struct scrinf *scrptr);
static VOID stgcli(struct scrinf *scrptr,CHAR *stg);
static VOID closlog(struct scrinf *scrptr,GBOOL keep);
static VOID sndcli(struct scrinf *scrptr,UINT len,CHAR *bytes);

INT (*scrpvec)(                    /* pump script interceptable vector     */
struct scrinf *scrptr)=dftpscr;    /*   which defaults to default pumper   */

static
struct scrcmd {                    /* script command structure             */
     CHAR cmd[SCMSIZ];             /*   command string (w/o params)        */
     INT (*cmdrou)(struct scrinf *scrptr); /* command handling routine     */
};

static
INT cmdhdl=-1;                     /* handle to dynamic array of commands  */

#define SCMINC      10             /* increment size for cmdhdl growth     */

static INT scrxit(struct scrinf *scrptr);
static INT scrgo2(struct scrinf *scrptr);
static INT scrtou(struct scrinf *scrptr);
static INT scrsnd(struct scrinf *scrptr);
static INT scrpau(struct scrinf *scrptr);
static INT scrwat(struct scrinf *scrptr);
static INT scrwfo(struct scrinf *scrptr);
static INT scrhup(struct scrinf *scrptr);
static INT scrmdm(struct scrinf *scrptr);
static INT scrusr(struct scrinf *scrptr);
static INT scrdsp(struct scrinf *scrptr);
static INT scrtrc(struct scrinf *scrptr);
static INT scrcse(struct scrinf *scrptr);
static INT scrlog(struct scrinf *scrptr);
static INT scremg(struct scrinf *scrptr);

VOID EXPORT
init__galscr(VOID)                 /* initialize the scripting API         */
{
     if (cmdhdl == -1) {
          cmdhdl=newarr(SCMINC,sizeof(struct scrcmd));
          register_scrcmd("EXIT",scrxit);
          register_scrcmd("GOTO",scrgo2);
          register_scrcmd("HANGUP",scrhup);
          register_scrcmd("MODEM",scrmdm);
          register_scrcmd("PAUSE",scrpau);
          register_scrcmd("SEND",scrsnd);
          register_scrcmd("TIMEOUT",scrtou);
          register_scrcmd("WAITFOR",scrwfo);
          register_scrcmd("WATCH",scrwat);
          register_scrcmd("USER",scrusr);
          register_scrcmd("DISPLAY",scrdsp);
          register_scrcmd("TRACE",scrtrc);
          register_scrcmd("CASE",scrcse);
          register_scrcmd("LOG",scrlog);
          register_scrcmd("ERROR",scremg);
     }
}

VOID EXPORT
initwc__galscr(VOID)
{
     init__galscr();
}

GBOOL
begscr(                            /* begin the processing of a script file*/
struct scrinf *scrptr,             /*   pointer to script info structure   */
CHAR *scrfil,                      /*   script file to process             */
VOID (*svrp)(                      /*   server output (can't be NULL)      */
     struct scrinf *scrptr,        /*     ptr to this structure            */
     UINT len,                     /*     number of bytes                  */
     CHAR *bytes),                 /*     bytes to output (NULs ok)        */
VOID (*clip)(                      /*   client / user output (NULL=none)   */
     struct scrinf *scrptr,        /*     ptr to this structure            */
     UINT len,                     /*     number of bytes                  */
     CHAR *bytes),                 /*     bytes to output (NULs ok)        */
VOID (*devp)(                      /*   device / modem command (NULL=none) */
     struct scrinf *scrptr,        /*     ptr to this structure            */
     CHAR *stg),                   /*     commands (NUL-term, ala btucmd())*/
VOID (*admp)(                      /*   admin / error output (NULL=none)   */
     struct scrinf *scrptr,        /*     ptr to this structure            */
     CHAR *stg))                   /*     error message (NUL-terminated)   */
{
     setmem(scrptr,sizeof(struct scrinf),0);
     if ((scrptr->scrfp=fopen(scrfil,FOPRA)) == NULL) {
          return(FALSE);
     }
     scrptr->svrp=svrp;
     scrptr->clip=clip == NULL ? clistb : clip;
     scrptr->devp=devp == NULL ? devstb : devp;
     scrptr->admp=admp == NULL ? admstb : admp;
     scrptr->flags=SCFUSR;                        /* default to USER ON */
     return(TRUE);
}

static VOID
clistb(                            /* stub client                          */
struct scrinf *scrptr,
UINT len,
CHAR *bytes)
{
     (VOID)scrptr;
     (VOID)len;
     (VOID)bytes;
}

static VOID
devstb(                            /* stub device command                  */
struct scrinf *scrptr,
CHAR *stg)
{
     (VOID)scrptr;
     (VOID)stg;
}

static VOID
admstb(                            /* stub admin report                    */
struct scrinf *scrptr,
CHAR *stg)
{
     (VOID)scrptr;
     (VOID)stg;
}

INT                                /*   CNTPSCR, ENDPSCR, or KILCHAN       */
pumpscr(                           /* pump the processing of a script      */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     return((*scrpvec)(scrptr));
}

static INT                         /*   CNTPSCR, ENDPSCR, or KILCHAN       */
dftpscr(                           /* default script pumper routine        */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     INT i,retval;
     ULONG tmpdur;
     struct scrcmd *cmdptr;

     if (scrptr->scrfp == NULL) {  /* ended already due to error (only way)*/
          return(KILCHAN);
     }
     if (scrptr->tosecs != 0) {
          tmpdur=scrptr->tosecs*65535L;
          if (hrtval()-scrptr->tostart >= tmpdur) {
               screrr(scrptr,spr("%u-SECOND TIMEOUT!",scrptr->tosecs));
               return(KILCHAN);
          }
     }
     if (scrptr->lfstgs[WFOIDX][0] != '\0') {
          return(CNTPSCR);
     }
     if (scrptr->pausecs != 0) {
          tmpdur=scrptr->pausecs*65535L;
          if (hrtval()-scrptr->paustart < tmpdur) {
               return(CNTPSCR);
          }
          scrptr->pausecs=0;
     }
     retval=CNTPSCR;
     while (1) {
          if (fgets(input,INPSIZ,scrptr->scrfp) != NULL) {
               depad(input);
               scrptr->lin++;
               stlcpy(scrptr->inplin,input,INPSIZ);
               if (input[0] != '\0'
                && input[0] != ';'
                && input[0] != ':') {
                    if (scrptr->flags&SCFTRC) {
                         stgcli(scrptr,spr("<line %d: %0.100s>\r\n",
                                           scrptr->lin,scrptr->inplin));
                    }
                    parsin();
                    for (i=0 ; i < ninarr(cmdhdl) ; i++) {
                         cmdptr=arrelem(cmdhdl,i);
                         if (sameas(margv[0],cmdptr->cmd)) {
                              retval=(*cmdptr->cmdrou)(scrptr);
                              break;
                         }
                    }
                    if (i == ninarr(cmdhdl)) {
                         screrr(scrptr,"BAD COMMAND ERROR!");
                         retval=KILCHAN;
                    }
                    break;
               }
               else {
                    parsin();      /* (needed in case of error message) */
               }
          }
          else {
               endscr(scrptr);
               retval=ENDPSCR;
               break;
          }
          if (scrptr->logfp != NULL && ferror(scrptr->logfp)) {
               screrr(scrptr,"ERROR WRITING LOG!");
          }
     }
     return(retval);
}

VOID
scrinp(                            /* handle input from script session     */
struct scrinf *scrptr,             /*   pointer to script info structure   */
UINT len,                          /*   length of input to handle          */
CHAR *inp)                         /*   pointer to input to handle         */
{
     INT i,lf;
     CHAR *cp,adjs[LFSSIZ],*adjp;
     CHAR svrc,lfsc;
     GBOOL ismatch;

     if (scrptr->scrfp == NULL) {  /* ended already, forgive and forget */
          return;
     }
     if (scrptr->flags&SCFUSR) {
          sndcli(scrptr,len,inp);
     }
     for (i=0,cp=inp ; i < len ; i++,cp++) {
          for (lf=0 ; lf < NUMLFS ; lf++) {
               if (scrptr->lfstgs[lf][0] != '\0') {
                    svrc=*cp;
                    lfsc=scrptr->lfstgs[lf][scrptr->lfcnts[lf]];
                    if (scrptr->flags&SCFCIG) {
                         ismatch=toupper(svrc) == toupper(lfsc);
                    }
                    else {
                         ismatch=svrc == lfsc;
                    }
                    if (ismatch) {
                         scrptr->lfcnts[lf]++;
                         if (scrptr->lfcnts[lf]
                          == strlen(scrptr->lfstgs[lf])) {
                              scrptr->lfcnts[lf]=0;
                              if (scrptr->lfstgs[WFOIDX][0] != '\0') {
                                   scrptr->lfcnts[WFOIDX]=0; /* clr WAITFOR */
                                   scrptr->lfstgs[WFOIDX][0]='\0';
                                   scrptr->lflbls[WFOIDX][0]='\0';
                              }
                              if (lf != WFOIDX) {
                                   if (!go2lbl(scrptr,scrptr->lflbls[lf])) {
                                        screrr(scrptr,"BAD LABEL ERROR!");
                                   }
                              }
                         }
                    }
                    else {
                         stlcpy(adjs,scrptr->lfstgs[lf],scrptr->lfcnts[lf]+1);
                         strcat(adjs,spr("%c",*cp));
                         for (adjp=adjs ; *adjp != '\0' ; adjp++) {
                              if (sameto(adjp,scrptr->lfstgs[lf])) {
                                   break;
                              }
                         }
                         scrptr->lfcnts[lf]=strlen(adjp);
                    }
               }
          }
     }
}

GBOOL
endscr(                            /* end the processing of a script file  */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     closlog(scrptr,TRUE);
     if (scrptr->scrfp != NULL) {
          fclose(scrptr->scrfp);
          setmem(scrptr,sizeof(struct scrinf),0);
          return(TRUE);
     }
     return(FALSE);
}

GBOOL
abtscr(                            /* abort the script (toss log if any)   */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     closlog(scrptr,FALSE);
     if (scrptr->scrfp != NULL) {
          fclose(scrptr->scrfp);
          setmem(scrptr,sizeof(struct scrinf),0);
          return(TRUE);
     }
     return(FALSE);
}

static VOID
closlog(                           /* close log file                       */
struct scrinf *scrptr,             /*   pointer to script info structure   */
GBOOL keep)                        /*   TRUE=keep new FALSE=preserve old   */
{
     if (scrptr->logfp != NULL) {
          fclose(scrptr->logfp);
          if (scrptr->logtmp[0] != '\0') {
               if (keep) {
                    unlink(scrptr->logpth);
                    rename(scrptr->logtmp,scrptr->logpth);
               }
               else {
                    unlink(scrptr->logtmp);
               }
          }
          scrptr->logfp=NULL;
     }
}

/* Utility routines */

VOID
register_scrcmd(                   /* register a script command            */
CHAR *cmd,                         /*   script command                     */
INT (*crou)(                       /*   script command handling routine    */
     struct scrinf *scrptr))       /*     script session info              */
{
     struct scrcmd newcmd;

     ASSERT(cmdhdl != -1);
     ASSERT(strlen(cmd) < SCMSIZ);
     stlcpy(newcmd.cmd,cmd,SCMSIZ);
     newcmd.cmdrou=crou;
     add2arr(cmdhdl,&newcmd);
}

VOID
screrr(                            /* report a script error                */
struct scrinf *scrptr,             /*   pointer to script info structure   */
CHAR *errmsg)                      /*   error msg (max length is MAXELN)   */
{
     CHAR *fmsg;

     ASSERTM(strlen(errmsg) <= MAXELN,errmsg);
     rstrin();
     scrptr->admp(scrptr,errmsg);
     if (scrptr->logfp != NULL) {
          fmsg=spr("%s  line %d: \"%0.60s\"\r\n",
                   errmsg,scrptr->lin,scrptr->inplin);
          fwrite(fmsg,1,strlen(fmsg),scrptr->logfp);
          fflush(scrptr->logfp);
     }
     endscr(scrptr);
}

static VOID
stgcli(                            /* send bytes to the client             */
struct scrinf *scrptr,             /*   pointer to script info structure   */
CHAR *stg)                         /*   pointer to NUL-terminated string   */
{
     sndcli(scrptr,strlen(stg),stg);
}

static VOID
sndcli(                            /* send bytes to the client             */
struct scrinf *scrptr,             /*   pointer to script info structure   */
UINT len,                          /*   length of output to handle         */
CHAR *bytes)                       /*   pointer to output to handle        */
{
     scrptr->clip(scrptr,len,bytes);
     if (scrptr->logfp != NULL) {
          fwrite(bytes,1,len,scrptr->logfp);
          fflush(scrptr->logfp);
     }
}

GBOOL
go2lbl(                            /* goto a label in the script file      */
struct scrinf *scrptr,             /*   pointer to script info structure   */
CHAR *lbl)                         /*   label to goto                      */
{
     CHAR tmplin[INPSIZ];

     rewind(scrptr->scrfp);
     scrptr->lin=0;
     scrptr->inplin[0]='\0';
     while (fgets(tmplin,INPSIZ,scrptr->scrfp) != NULL) {
          depad(tmplin);
          scrptr->lin++;
          stlcpy(scrptr->inplin,tmplin,INPSIZ);
          if (tmplin[0] == ':' && sameas(lbl,firstwd(tmplin+1))) {
               return(TRUE);
          }
     }
     return(FALSE);
}

VOID
psnstg(                            /* prepare string for send              */
CHAR *stgptr)                      /*   pointer to string to prepare       */
{
     while (*stgptr != '\0') {
          if (*stgptr == '^' && *(stgptr+1) != '\0') {
               stgptr++;
               if (*stgptr != '@') {
                    *stgptr=toupper(*stgptr)-'@';
               }
               movmem(stgptr,stgptr-1,strlen(stgptr)+1);
          }
          stgptr++;
     }
}

/* Built-in script command handlers */

static INT
scrxit(                            /* EXIT command handler routine         */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     endscr(scrptr);
     return(ENDPSCR);
}

static INT
scrgo2(                            /* GOTO command handler routine         */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     if (margc > 1) {
          if (!go2lbl(scrptr,margv[1])) {
               screrr(scrptr,"BAD LABEL ERROR!");
               return(KILCHAN);
          }
          return(CNTPSCR);
     }
     screrr(scrptr,"MISSING PARAMETER!");
     return(KILCHAN);
}

static INT
scrtou(                            /* TIMEOUT command handler routine      */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     if (margc > 1) {
          scrptr->tosecs=atoi(margv[1]);
          scrptr->tostart=hrtval();
     }
     else {
          scrptr->tosecs=0;
     }
     return(CNTPSCR);
}

static INT
scrsnd(                            /* SEND command handler routine         */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     rstrin();
     psnstg(margv[1]);
     (*scrptr->svrp)(scrptr,strlen(margv[1]),margv[1]);
     return(CNTPSCR);
}

static INT
scrpau(                            /* PAUSE command handler routine        */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     if (margc > 1) {
          scrptr->pausecs=atoi(margv[1]);
          scrptr->paustart=hrtval();
          return(CNTPSCR);
     }
     screrr(scrptr,"MISSING PARAMETER!");
     return(KILCHAN);
}

static INT
scrwat(                            /* WATCH command handler routine        */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     INT wn;

     if (margc < 2 || margc == 3) {
          screrr(scrptr,"MISSING PARAMETER!");
          return(KILCHAN);
     }
     wn=atoi(margv[1])-1;
     if (wn < 0 || wn >= NUMWAT) {
          screrr(scrptr,"WATCH NUMBER ERROR!");
          return(KILCHAN);
     }
     scrptr->lfcnts[wn]=0;
     if (margc > 2) {
          if (strlen(margv[2]) >= LFLSIZ) {
               screrr(scrptr,"LABEL TOO LONG ERROR!");
               return(KILCHAN);
          }
          strcpy(scrptr->lflbls[wn],margv[2]);
          rstrin();
          if (strlen(margv[3]) >= LFSSIZ) {
               screrr(scrptr,"WATCH TOO LONG ERROR!");
               return(KILCHAN);
          }
          strcpy(scrptr->lfstgs[wn],margv[3]);
     }
     else {
          scrptr->lflbls[wn][0]='\0';
          scrptr->lfstgs[wn][0]='\0';
     }
     return(CNTPSCR);
}

static INT
scrwfo(                            /* WAITFOR command handler routine      */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     if (margc != 2) {
          screrr(scrptr,"INVALID PARAMS ERROR!");
          return(KILCHAN);
     }
     if (strlen(margv[1]) >= LFSSIZ) {
          screrr(scrptr,"STRING TOO LONG ERROR!");
          return(KILCHAN);
     }
     strcpy(scrptr->lfstgs[WFOIDX],margv[1]);
     scrptr->lflbls[WFOIDX][0]='\0';
     scrptr->lfcnts[WFOIDX]=0;
     return(CNTPSCR);
}

static INT
scrhup(                            /* HANGUP command handler routine       */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     endscr(scrptr);
     return(KILCHAN);
}

static INT
scrmdm(                            /* MODEM command handler routine        */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     INT retval;

     if (margc < 2) {
          screrr(scrptr,"MISSING PARAMETER!");
          retval=KILCHAN;
     }
     else {
          rstrin();
          scrptr->devp(scrptr,margv[1]);
          retval=CNTPSCR;
     }
     return(retval);
}

static INT
scrusr(                            /* USER command handler routine         */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     INT retval;

     if (margc == 2 && sameas(margv[1],"ON")) {
          scrptr->flags|=SCFUSR;
          retval=CNTPSCR;
     }
     else if (margc == 2 && sameas(margv[1],"OFF")) {
          scrptr->flags&=~SCFUSR;
          retval=CNTPSCR;
     }
     else {
          screrr(scrptr,"BAD PARAMETER ERROR!");
          retval=KILCHAN;
     }
     return(retval);
}

static INT
scrdsp(                            /* DISPLAY command handler routine      */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     INT retval;

     if (margc < 2) {
          screrr(scrptr,"MISSING PARAMETER!");
          retval=KILCHAN;
     }
     else {
          rstrin();
          psnstg(margv[1]);
          stgcli(scrptr,margv[1]);
          retval=CNTPSCR;
     }
     return(retval);
}

static INT
scrtrc(                            /* TRACE command handler routine        */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     INT retval;

     if (margc == 2 && sameas(margv[1],"ON")) {
          scrptr->flags|=SCFTRC;
          retval=CNTPSCR;
     }
     else if (margc == 2 && sameas(margv[1],"OFF")) {
          scrptr->flags&=~SCFTRC;
          retval=CNTPSCR;
     }
     else {
          screrr(scrptr,"BAD PARAMETER ERROR!");
          retval=KILCHAN;
     }
     return(retval);
}

static INT
scrcse(                            /* CASE command handler routine         */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     INT retval;

     if (margc == 2 && sameas(margv[1],"IGNORE")) {
          scrptr->flags|=SCFCIG;
          retval=CNTPSCR;
     }
     else if (margc == 2 && sameas(margv[1],"MATTERS")) {
          scrptr->flags&=~SCFCIG;
          retval=CNTPSCR;
     }
     else {
          screrr(scrptr,"BAD PARAMETER ERROR!");
          retval=KILCHAN;
     }
     return(retval);
}

static INT
scrlog(                            /* LOG command handler routine          */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     INT retval;

     if (margc == 2 && sameas(margv[1],"OFF")) {
          closlog(scrptr,TRUE);
          retval=CNTPSCR;
     }
     else if (margc == 2 && sameas(margv[1],"ABORT")) {
          closlog(scrptr,FALSE);
          retval=CNTPSCR;
     }
     else if (margc == 2 || margc == 3 && sameas("APPEND",margv[1])) {
          closlog(scrptr,TRUE);
          stlcpy(scrptr->logpth,margv[margc-1],GCMAXPTH);
          if (margc == 3) {
               scrptr->logtmp[0]='\0';
               scrptr->logfp=fopen(scrptr->logpth,FOPAB);
          }
          else {
               fileparts(GCPART_PATH,scrptr->logpth,
                                     scrptr->logtmp,GCMAXPTH-8-1-3);
               strcat(scrptr->logtmp,"\\"LOGTFN);
               uniqfn(scrptr->logtmp);
               scrptr->logfp=fopen(scrptr->logtmp,FOPWB);
          }
          scrptr->inplin[0]='\0';
          if (scrptr->logfp == NULL) {
               screrr(scrptr,"CAN'T CREATE LOG FILE!");
               retval=KILCHAN;
          }
          else {
               fprintf(scrptr->logfp,
                       "\r\n--- LOGGING SCRIPT TO %s ON %s %s ---\r\n",
                       scrptr->logpth,ncdate(today()),nctime(now()));
               retval=CNTPSCR;
          }
     }
     else {
          screrr(scrptr,"BAD PARAMETER ERROR!");
          retval=KILCHAN;
     }
     return(retval);
}

static INT
scremg(                            /* ERROR command handler routine        */
struct scrinf *scrptr)             /*   pointer to script info structure   */
{
     rstrin();
     screrr(scrptr,"ERROR ENCOUNTERED!");
     return(KILCHAN);
}
