/***************************************************************************
 *                                                                         *
 *   TLCACT.CPP                                                            *
 *                                                                         *
 *   Copyright (c) 1987-1998 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Entertainment Collection Teleconference action handling routines.     *
 *                                                                         *
 *                                           - M. Donnelly  11/3/93        *
 *   Mods for unified Teleconference         - P. Henning   8/12/97        *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "tlcapi.hpp"

#define FILREV "$Revision: 41 $"

const INT CSNOPOP        =0;
const INT CSNOCOM        =1;

const INT AF_STDDIR      =0;      /* Standard direction (@TU = "Sysop")    */
const INT AF_DIROTH      =1;      /* Directed at target (@TU = "you")      */
const INT AF_DIRALL      =2;      /* Directed at all    (@TU = "everyone") */
const INT AF_ACTHELP     =4;      /* Action help special case.             */

#define ABLEFT (ABFSIZ-sizeof(KILCOLR)-1-strlen(afmt)) /* Room in format buf*/
const INT SNDSIZ         =31;     /* filename + date + time + tabs         */

DFAFILE *dfaAct;

GBOOL actSecret=FALSE;
GBOOL actHasFile=FALSE;

LPACTION  tmpact;
LPACTLST  tmplst;

CHAR* afmt=NULL;
CHAR asound[SNDSIZ];               /* name of sound file to play           */

const CHAR* actTmpTo=NULL;
VOID loadacts(VOID);
CHAR *ads2dst(CHAR *dest,CHAR *src);
CHAR *adc2dst(CHAR *dest,CHAR src);
GBOOL adf2dst(CHAR *src,INT direct);
// sound stuff currently not implemented
//static GBOOL plysnd(CHAR *src);
//static VOID shosnd(INT tlcchn,INT exunm);
//static VOID sndone(INT unum);

VOID (*actionAddHook)(LPACTION);
VOID (*actionDeleteHook)(LPACTION);
VOID (*actionChangeHook)(LPACTION,LPACTION);
VOID (*listAddHook)(LPACTLST);
VOID (*listDeleteHook)(LPACTLST);
VOID (*listChangeHook)(LPACTLST,LPACTLST);

CHAR *
actfmt(                            /* parse action macros                  */
CHAR *src,
INT direct,
CTlcUser* pUser=NULL);

static VOID
actHandleResp(                     // handle response to an action
INT DirType,                       //   direction type (normal/all)
SHORT RespType,                    //   response message type (normal/secret)
CTlcUser* pToUser,                 //   user at whom action is directed
CHAR* RespFormat,                  //   response format string
const CHAR* AllText,               //   text displayed to everyone else
const CHAR* arg=NULL);             //   additional argument

/* function definitions begin here */

MARKSOURCE(tlcact)

static VOID
dftactionHook(                     /* default action add/del hookfunc      */
LPACTION)
{
}

static VOID
dftactionChangeHook(              /* default action chg hookfunc           */
LPACTION,
LPACTION)
{
}

static VOID
dftlistHook(                      /* default list add/del hookfunc         */
LPACTLST)
{
}

static VOID
dftlistChangeHook(                /* default list chg hookfunc             */
LPACTLST,
LPACTLST)
{
}

GBOOL
iscsu(VOID)
{
     return(usrptr->flags&ISGCSU ? TRUE : FALSE);
}

VOID
iniact(VOID)                       /* Initialize the action routines       */
{
     dfaAct=dfaOpen("galtela.dat",ACTRECSIZ,NULL);
     tmpact=(LPACTION)alczer(sizeof(ACTION));
     tmplst=(LPACTLST)alczer(sizeof(ACTLST));
     loadacts();
     actionAddHook=dftactionHook;
     actionDeleteHook=dftactionHook;
     actionChangeHook=dftactionChangeHook;
     listAddHook=dftlistHook;
     listDeleteHook=dftlistHook;
     listChangeHook=dftlistChangeHook;
     afmt=(CHAR*)alczer(sizeof(CHAR)*ABFSIZ);
}

VOID
loadacts(VOID)                     /* Load in the action lists             */
{
     LPACTLST tmpptr;

     dfaSetBlk(dfaAct);
     if (dfaQueryGE(LSTNAM,1)) {
          do {
               dfaAbsRec(NULL,1);
               tmpptr=reinterpret_cast<LPACTLST>(dfaAct->data);
               if (!sameas(tmpptr->list,LSTNAM)) {
                    break;
               }
               tlcAPI->actCreate(tmpptr);
          } while (dfaQueryNX());
     }
     dfaRstBlk();
}

VOID
clsact(VOID)                       /* Close down the action routines       */
{
     CTlcAList* pList;
     ACTLST lststr;

     dfaSetBlk(dfaAct);

     pList=tlcAPI->actGetFirst(TRUE);
     while (pList != NULL) {
          stlcpy(lststr.list,LSTNAM,ACTSIZ);
          stlcpy(lststr.name,pList->GetName(),ACTSIZ);
          if (pList->IsDeleted()) {
               while (dfaAcqEQ(NULL,pList->GetName(),1)) {
                    dfaDelete();
               }
               if (dfaAcqEQ(NULL,&lststr,0)) {
                    dfaDelete();
               }
          }
          tlcAPI->actRemove(pList->GetName());
          pList=tlcAPI->actGetFirst(TRUE);
     }
     dfaRstBlk();
     dfaClose(dfaAct);
     if (afmt != NULL) {
          free(afmt);
          afmt=NULL;
     }
}

INT EXPORT
addact(                            /* Add a new action                     */
LPACTION newact)
{
     CTlcAList* list=tlcAPI->actGetByName(newact->list);

     if (list == NULL) {
          return(1);
     }
     dfaSetBlk(dfaAct);
     if (dfaQueryEQ(newact,0)) {
          dfaRstBlk();
          return(1);
     }
     strlwr(newact->name);
     strupr(newact->list);
     if (strstr(newact->complx,"@IN")) {
          newact->isyell=1;
     }
     else {
          newact->isyell=0;
     }
     if (dfaAcqEQ(NULL,newact,0)) {
          dfaDelete();
     }
     memset(dfaAct->data,0,ACTRECSIZ);
     memcpy(dfaAct->data,newact,sizeof(ACTION));
     dfaInsert(NULL);
     actionAddHook(newact);
     dfaRstBlk();
     return(0);
}

INT EXPORT
addlst(                            /* Add a new action list                */
LPACTLST newlst)
{
     CTlcAList* list=tlcAPI->actGetByName(newlst->name);

     if (list != NULL) {
          return(1);
     }
     strupr(newlst->name);
     dfaSetBlk(dfaAct);
     strcpy(newlst->list,LSTNAM);
     if (dfaAcqEQ(NULL,newlst->list,0)) {
          dfaDelete();
     }
     memset(dfaAct->data,0,ACTRECSIZ);
     memcpy(dfaAct->data,newlst,sizeof(ACTLST));
     tlcAPI->actCreate(newlst);
     dfaInsert(NULL);
     listAddHook(newlst);
     dfaRstBlk();
     return(0);
}

INT EXPORT
delact(                            /* Remove an action                     */
LPACTION oldact)
{
     CTlcAList* list=tlcAPI->actGetByName(oldact->list);

     if (list == NULL) {
          return(1);
     }
     if (!list->wordExists(oldact->name)) {
          return(1);
     }
     strlwr(oldact->name);
     strupr(oldact->list);
     dfaSetBlk(dfaAct);
     if (dfaAcqEQ(NULL,oldact,0)) {
          dfaDelete();
          actionDeleteHook(oldact);
     }
     dfaRstBlk();
     return(0);
}

INT EXPORT
chgact(                            /* Change an action                     */
LPACTION newact)
{
     CTlcAList* list;
     list=tlcAPI->actGetByName(newact->list);

     if (list == NULL) {
          return(1);
     }
     if (!list->wordExists(newact->name)) {
          return(1);
     }
     strlwr(newact->name);
     strupr(newact->list);
     if (strstr(newact->complx,"@IN")) {
          newact->isyell=1;
     }
     else {
          newact->isyell=0;
     }
     dfaSetBlk(dfaAct);
     dfaGetEQ(NULL,newact->list,0);
     actionChangeHook(newact,(LPACTION)(dfaAct->data));
     memset(dfaAct->data,0,ACTRECSIZ);
     memcpy(dfaAct->data,newact,sizeof(ACTION));
     dfaUpdate(NULL);

     dfaRstBlk();
     return(0);
}

INT EXPORT
chglst(                            /* Change an action list                */
LPACTLST newlst)
{
     CTlcAList* list=tlcAPI->actGetByName(newlst->name);

     if (list == NULL) {
          return(1);
     }
     strupr(newlst->name);
     strcpy(newlst->list,LSTNAM);
     dfaSetBlk(dfaAct);
     dfaGetEQ(NULL,newlst,0);
     listChangeHook(newlst,(LPACTLST)(dfaAct->data));
     memset(dfaAct->data,0,ACTRECSIZ);
     memcpy(dfaAct->data,newlst,sizeof(ACTLST));
     dfaUpdate(NULL);
     dfaRstBlk();
     list->SetConfig(newlst);
     return(0);
}

CHAR *
actnam(                            /* appropriate "name" to direct to      */
INT direct,
CTlcUser* pUser)
{
     switch (direct) {
     case AF_DIROTH:
          return("you");
     case AF_ACTHELP:
          return("Sysop");
     case AF_DIRALL:
          return("everyone");
     case AF_STDDIR:
          return(const_cast<CHAR*>(pUser->GetName()));
     default:
          return("[??]");
     }
}

CHAR *
actpos(                            /* appropriate "possessive" to direct to*/
INT direct,
CTlcUser* pUser)
{
     switch (direct) {
     case AF_DIROTH:
          return("your");
     case AF_ACTHELP:
          return("Sysop's");
     case AF_DIRALL:
          return("everyone's");
     case AF_STDDIR:
          return(spr("%s's",pUser->GetName()));
     default:
          return("[??]");
     }
}

CHAR *
himher(                            /* appropriate pronoun                  */
INT direct,
CTlcUser* pUser)
{
     switch (direct) {
     case AF_DIROTH:
          return("you");
     case AF_ACTHELP:
          if (pUser == NULL) {
               return("him");
          }
          return(pUser->GetSex() == 'F' ? "her" : "him");
     case AF_DIRALL:
          return("them");
     case AF_STDDIR:
          return(pUser->GetSex() == 'F' ? "her" : "him");
     default:
          return("[??]");
     }
}

CHAR *
hisher(                            /* possessive pronoun                   */
INT direct,
CTlcUser* pUser)
{
     switch (direct) {
     case AF_DIROTH:
          return("your");
     case AF_ACTHELP:
          if (pUser == NULL) {
               return("his");
          }
          return(pUser->GetSex() == 'F' ? "her" : "his");
     case AF_DIRALL:
          return("their");
     case AF_STDDIR:
          return(pUser->GetSex() == 'F' ? "her" : "his");
     default:
          return("[??]");
     }
}

/* Direct can be:

   AF_STDDIR:  not directed (others see) (@TU/@TS/@TM -> Sysop/his/him)
   AF_DIROTH:  directed at victim        (@TU/@TS/@TM -> you/your/you)
   AF_DIRALL:  to everyone               (@TU/@TS/@TM -> everyone/their/them)
   AF_ACTHELP: action help               (@TU/@TS/@TM -> Sysop/his/him)    */

CHAR *
actfmt(                            /* parse action macros                  */
CHAR *src,
INT direct,
CTlcUser* pUser)
{
     INT sndctr,i;
     CHAR *tmp,ascbuf[4];
     static INT blink,rcsctr=0;
     static CHAR *dest,fore[7],back[6];

     sndctr=0;
     if (rcsctr++ == 0) {
          dest=afmt;
          i=strlen(src)-1;
          while (i >= 0 && src[i] == '\1') {
               src[i]='\0';
               i--;
          }
          strrpl(src,'\1','\n');
          blink=0;
          strcpy(fore,"[1;32m");
          strcpy(back,"[40m");
          setmem(afmt,ABFSIZ,0);
          setmem(asound,SNDSIZ,0);
     }
     while (*src != '\0') {
          if (*src != '@') {
               dest=adc2dst(dest,*src);
               src++;
          }
          else {
               src++;
               switch (*src) {
               case '@':
                    dest=adc2dst(dest,*src);
                    src++;
                    break;
               case '0':
               case '1':
               case '2':
               case '3':
               case '4':
               case '5':
               case '6':
               case '7':
               case '8':
               case '9':
                    ascbuf[0]=src[0];
                    i=1;
                    while (i < 3 && isdigit(src[i])) {
                         ascbuf[i]=src[i];
                         i++;
                    }
                    ascbuf[i]='\0';
                    dest=adc2dst(dest,atoi(ascbuf));
                    src+=i;
                    break;
               case 'E':
                    dest=adc2dst(dest,'\33');
                    src++;
                    break;
               case 'N':
                    dest=adc2dst(dest,'\r');
                    src++;
                    break;
               case 'C':
                    src++;
                    if (*src == '\n') {
                         src++;
                    }
                    break;
               case 'A':
                    src++;
                    switch (*src) {
                    case '0':
                         dest=ads2dst(dest,"[0m");
                         dest=ads2dst(dest,back);
                         dest=ads2dst(dest,fore);
                         blink=0;
                         break;
                    case '1':
                         dest=ads2dst(dest,"[5m");
                         blink=1;
                         break;
                    default:
                         dest=ads2dst(dest,UNKMAC);
                         break;
                    }
                    src++;
                    break;
               case 'B':
                    src++;
                    switch (*src) {
                    case '0':
                    case '8':
                         dest=ads2dst(dest,"[40m");
                         strcpy(back,"[40m");
                         break;
                    case '1':
                    case '9':
                         dest=ads2dst(dest,"[41m");
                         strcpy(back,"[41m");
                         break;
                    case '2':
                    case 'A':
                         dest=ads2dst(dest,"[42m");
                         strcpy(back,"[42m");
                         break;
                    case '3':
                    case 'B':
                         dest=ads2dst(dest,"[43m");
                         strcpy(back,"[43m");
                         break;
                    case '4':
                    case 'C':
                         dest=ads2dst(dest,"[44m");
                         strcpy(back,"[44m");
                         break;
                    case '5':
                    case 'D':
                         dest=ads2dst(dest,"[45m");
                         strcpy(back,"[45m");
                         break;
                    case '6':
                    case 'E':
                         dest=ads2dst(dest,"[46m");
                         strcpy(back,"[46m");
                         break;
                    case '7':
                    case 'F':
                         dest=ads2dst(dest,"[47m");
                         strcpy(back,"[47m");
                         break;
                    default:
                         dest=ads2dst(dest,UNKMAC);
                         break;
                    }
                    src++;
                    break;
               case 'F':
                    src++;
                    switch (*src) {
                    case 'P':
                         dest=ads2dst(dest,const_cast<CHAR*>(curUser->GetName()));
                         dest=ads2dst(dest,"'s");
                         break;
                    case 'U':
                         dest=ads2dst(dest,const_cast<CHAR*>(curUser->GetName()));
                         break;
                    case 'S':
                         dest=ads2dst(dest,hisher(0,curUser));
                         break;
                    case 'M':
                         dest=ads2dst(dest,himher(0,curUser));
                         break;
                    default:
                         if ((*src >= 'A' && *src <= 'F') || isdigit(*src)) {
                              switch (*src) {
                              case '0':
                                   dest=ads2dst(dest,"[0;30");
                                   strcpy(fore,"[0;30");
                                   break;
                              case '1':
                                   dest=ads2dst(dest,"[0;31");
                                   strcpy(fore,"[0;31");
                                   break;
                              case '2':
                                   dest=ads2dst(dest,"[0;32");
                                   strcpy(fore,"[0;32");
                                   break;
                              case '3':
                                   dest=ads2dst(dest,"[0;33");
                                   strcpy(fore,"[0;33");
                                   break;
                              case '4':
                                   dest=ads2dst(dest,"[0;34");
                                   strcpy(fore,"[0;34");
                                   break;
                              case '5':
                                   dest=ads2dst(dest,"[0;35");
                                   strcpy(fore,"[0;35");
                                   break;
                              case '6':
                                   dest=ads2dst(dest,"[0;36");
                                   strcpy(fore,"[0;36");
                                   break;
                              case '7':
                                   dest=ads2dst(dest,"[0;37");
                                   strcpy(fore,"[0;37");
                                   break;
                              case '8':
                                   dest=ads2dst(dest,"[1;30");
                                   strcpy(fore,"[1;30");
                                   break;
                              case '9':
                                   dest=ads2dst(dest,"[1;31");
                                   strcpy(fore,"[1;31");
                                   break;
                              case 'A':
                                   dest=ads2dst(dest,"[1;32");
                                   strcpy(fore,"[1;32");
                                   break;
                              case 'B':
                                   dest=ads2dst(dest,"[1;33");
                                   strcpy(fore,"[1;33");
                                   break;
                              case 'C':
                                   dest=ads2dst(dest,"[1;34");
                                   strcpy(fore,"[1;34");
                                   break;
                              case 'D':
                                   dest=ads2dst(dest,"[1;35");
                                   strcpy(fore,"[1;35");
                                   break;
                              case 'E':
                                   dest=ads2dst(dest,"[1;36");
                                   strcpy(fore,"[1;36");
                                   break;
                              case 'F':
                                   dest=ads2dst(dest,"[1;37");
                                   strcpy(fore,"[1;37");
                                   break;
                              }
                              if (blink) {
                                   dest=ads2dst(dest,";5");
                              }
                              dest=adc2dst(dest,'m');
                              strcat(fore,"m");
                         }
                         else {
                              dest=ads2dst(dest,UNKMAC);
                         }
                         break;
                    }
                    src++;
                    break;
               case 'T':
                    src++;
                    if ((pUser == NULL) && (direct != AF_ACTHELP)
                     && (direct != AF_DIRALL)) {
                         dest=ads2dst(dest,"(No @T context!)");
                         src++;
                         break;
                    }
                    switch (*src) {
                    case 'P':
                         dest=ads2dst(dest,actpos(direct,pUser));
                         break;
                    case 'U':
                         dest=ads2dst(dest,actnam(direct,pUser));
                         break;
                    case 'S':
                         dest=ads2dst(dest,hisher(direct,pUser));
                         break;
                    case 'M':
                         dest=ads2dst(dest,himher(direct,pUser));
                         break;
                    default:
                         dest=ads2dst(dest,UNKMAC);
                         break;
                    }
                    src++;
                    break;
               case 'I':
                    src++;
                    if (*src == 'N') {
                         if (direct == AF_ACTHELP) {
                              dest=ads2dst(dest,"(User input here)");
                         }
                         else {
                              if (margc > 1) {
                                   rstrin();
                                   dest=ads2dst(dest,margv[1]);
                                   parsin();
                              }
                         }
                    }
                    else {
                         dest=ads2dst(dest,UNKMAC);
                    }
                    src++;
                    break;
               case '<':
                    actHasFile=TRUE;
                    src++;
                    if (rcsctr != 1) {
                         dest=ads2dst(dest,"(No recursed files)");
                    }
                    else if ((tmp=strchr(src,'>')) == NULL) {
                         dest=ads2dst(dest,UNKMAC);
                    }
                    else {
                         *tmp='\0';
                         if (!adf2dst(src,direct)) {
                              dest=ads2dst(dest,"(File not found)");
                         }
                         *tmp='>';
                         src=tmp+1;
                    }
                    break;
               case '(':
// sounds currently not supported; this case is here to preserve
// backward compatibility with actions created for the old Entertainment
// Teleconference.
                    if ((tmp=strchr(src,')')) == NULL) {
                         ++src;
                    }
                    else {
                         src=tmp+1;
                    }
                    (VOID)sndctr;
#if 0 // old sound handling code
                    actHasFile=TRUE;
                    src++;
                    sndctr++;
                    if (rcsctr != 1) {
                         dest=ads2dst(dest,"(No sounds in files)");
                    }
                    else if (sndctr != 1) {
                         dest=ads2dst(dest,"(One sound per action)");
                    }
                    else if ((tmp=strchr(src,')')) == NULL) {
                         dest=ads2dst(dest,UNKMAC);
                    }
                    else {
                         *tmp='\0';
                         if (!plysnd(src)) {
                              dest=ads2dst(dest,"(File not found)");
                         }
                         *tmp=')';
                         src=tmp+1;
                    }
#endif // old sound code
                    break;
               default:
                    dest=ads2dst(dest,UNKMAC);
                    src++;
                    break;
               }
          }
     }
     if (--rcsctr == 0) {
          strcat(dest,KILCOLR);
     }
     xlttxv(afmt,ABFSIZ);
     return(afmt);
}

CHAR *
ads2dst(                           /* Add macro expansion string to dest   */
CHAR *dest,                        /*   destination pointer(inside formatbuf)*/
CHAR *src)                         /*   string to add to dest              */
{
     stlcpy(dest,src,ABLEFT);
     return(dest+strlen(dest));
}

CHAR *
adc2dst(                           /* Add macro expansion character to dest*/
CHAR *dest,                        /*   destination pointer(inside formatbuf)*/
CHAR src)                          /*   character to add to dest           */
{
     if (ABLEFT > 0) {
          *dest++=src;
     }
     return(dest);
}

GBOOL
adf2dst(                           /* Add macro expansion file to dest     */
CHAR *src,                         /*   file to add to dest                */
INT direct)                        /*   direct parameter for actfmt()      */
{
     FILE *fp;
     CHAR linbuf[255];

     if (rsvnam(src)) {
          return(FALSE);
     }
     if ((fp=fopen(src,FOPRA)) != NULL) {
          while (fgets(linbuf,sizeof(linbuf),fp) != NULL && ABLEFT > 0) {
               actfmt(linbuf,direct,NULL);
          }
          fclose(fp);
          return(TRUE);
     }
     return(FALSE);
}

INT EXPORT
isempty(                           /* check for an empty action            */
const CHAR *targ)
{
     return(sameas(targ,"\1\1\1"));
}

VOID EXPORT
acthlp(VOID)                       /* Perform action help for cur action   */
{
     if (isempty(tmpact->complx)) {             /* Simple action         */
          prfmsg(SIMPHLP3,tmpact->name,actfmt(tmpact->simple,AF_ACTHELP));
          return;
     }
     else {
          othusn=-1;
          if (isempty(tmpact->simple)) {         /* Forced yell or direct */
               if (tmpact->isyell) {
                    prfmsg(YELLHLP3,tmpact->name,tmpact->name,
                           actfmt(tmpact->complx,AF_ACTHELP));
               }
               else {
                    prfmsg(DIRHLP3,tmpact->name,tmpact->name,
                           actfmt(tmpact->complx,AF_ACTHELP));
               }
          }
          else {
               if (tmpact->isyell) {
                    prfmsg(CYELHLP4,tmpact->name,tmpact->name,
                           actfmt(tmpact->simple,AF_ACTHELP));
                    prfmsg(CYELHLP5,tmpact->name,tmpact->name,
                           actfmt(tmpact->complx,AF_ACTHELP));
               }
               else {
                    prfmsg(CDIRHLP4,tmpact->name,tmpact->name,
                           actfmt(tmpact->simple,AF_ACTHELP));
                    prfmsg(CDIRHLP5,tmpact->name,tmpact->name,
                           actfmt(tmpact->complx,AF_ACTHELP));
               }
          }
     }
}

INT EXPORT
asimpl(VOID)                       /* Process simple action command        */
{
     othusn=-1;     /* actfmt() will catch this and error on @T's          */
     if (isempty(tmpact->simple)) {     /* No simple for this action!?     */
          prfmsg(tmpact->isyell ? MUSTYEL2 : MUSTDIR2,tmpact->name,tmpact->name);
          curUser->OnRecvText(SYSUID,NULL,NULL,RECV_TYPE_PRFBUF);
          clrprf();
          return(CSNOPOP);
     }
     else {
          const CHAR* AllText=actfmt(tmpact->simple,AF_STDDIR,curUser);
          curChannel->PublicSendBut(curUser->GetName(),NULL,AllText
                                   ,RECV_TYPE_ACTION,ACTMSG_OUT,NULL,NULL,NULL
                                   ,curUser->GetName());
          actHandleResp(AF_STDDIR,ACTMSG_RES,curUser,tmpact->resp,AllText);
          return(CSNOCOM);
     }
}

INT EXPORT
acomplx(VOID)                      /* Process complex action command       */
{
     INT count;
     INT secret=0;
     INT actresp=CSNOCOM;
     CTlcUser* pToUser;

     if (tmpact->isyell) {
          othusn=-1;     /* actfmt() will catch this and error on @T's     */
          const CHAR* AllText=actfmt(tmpact->complx,AF_STDDIR,curUser);
          curChannel->PublicSendBut(curUser->GetName(),NULL,AllText
                                   ,RECV_TYPE_ACTION,ACTMSG_OUT,NULL,NULL,NULL
                                   ,curUser->GetName());
          actHandleResp(AF_STDDIR,ACTMSG_RES,curUser,tmpact->resp,AllText);
     }
     else {
          if (sameas(margv[margc-1],"secretly")) {
               secret=1;
               margn[margc-2][0]='\0';
          }
          if (sameas(margv[1],"all")) {
               if (secret) {
                    prfmsg(NAASEC2,tmpact->name);
                    curUser->OnRecvText(SYSUID,NULL,NULL,RECV_TYPE_PRFBUF);
                    clrprf();
                    actresp=CSNOPOP;
               }
               else {
                    actTmpTo=SYSUID;
                    const CHAR* AllText=actfmt(tmpact->complx,AF_DIRALL);
                    curChannel->PublicSendBut(curUser->GetName(),NULL,AllText
                                             ,RECV_TYPE_ACTION,ACTMSG_OUT
                                             ,NULL,NULL,NULL
                                             ,curUser->GetName());
                    actHandleResp(AF_DIRALL,ACTMSG_RES,NULL
                                 ,tmpact->resp,AllText);
               }
          }
          else {
               rstrin();
               strcpy(input,margv[1]);
               parsin();
               curChannel->FindUser(margv[0],&count,&pToUser);
               if (count == 0) {
                    prfmsg(WHSNHR3);
                    curUser->OnRecvText(SYSUID,NULL,NULL,RECV_TYPE_PRFBUF);
                    actresp=CSNOPOP;
                    clrprf();
               }
               else if (count > 1) {
                    prfmsg(AMBIG2,margv[0],tmpact->name);
                    curUser->OnRecvText(SYSUID,NULL,NULL,RECV_TYPE_PRFBUF);
                    actresp=CSNOPOP;
                    clrprf();

               }
               else if (pToUser->CheckForget(curUser)) {
                    prfmsg(FORGTN2,pToUser->GetName());
                    curUser->OnRecvText(SYSUID,NULL,NULL,RECV_TYPE_PRFBUF);
                    actresp=CSNOPOP;
                    clrprf();
               }
               else if (pToUser->CheckIgnore(curUser)) {
                    prfmsg(IGNORN3,pToUser->GetName());
                    curUser->OnRecvText(SYSUID,NULL,NULL,RECV_TYPE_PRFBUF);
                    actresp=CSNOPOP;
                    clrprf();
               }
               else if (teleIsLocal(pToUser)
                     && !pToUser->CheckAccess(tmpAList->GetConfigPtr()->seekey)) {
                    prfmsg(CNTSEE3,pToUser->GetName(),tmpact->name);
                    curUser->OnRecvText(SYSUID,NULL,NULL,RECV_TYPE_PRFBUF);
                    actresp=CSNOPOP;
                    clrprf();
               }
               else if (secret) {
                    actTmpTo=pToUser->GetName();
                    actSecret=TRUE;
                    pToUser->OnRecvText(curUser->GetName(),NULL
                                       ,actfmt(tmpact->complx,AF_DIROTH,pToUser)
                                       ,RECV_TYPE_ACTION,ACTMSG_OUT_SECRET);
                    actSecret=FALSE;
                    actTmpTo=NULL;
                    actHandleResp(AF_STDDIR,ACTMSG_RES_SECRET,pToUser
                                 ,tmpact->resp
                                 ,actfmt(tmpact->complx,AF_STDDIR,pToUser)
                                 ,pToUser->GetName());
               }
               else {
                    actTmpTo=pToUser->GetName();
                    pToUser->OnRecvText(curUser->GetName(),NULL
                                       ,actfmt(tmpact->complx,AF_DIROTH,pToUser)
                                       ,RECV_TYPE_ACTION,ACTMSG_OUT);
                    const CHAR* AllText=actfmt(tmpact->complx,AF_STDDIR,pToUser);
                    if (teleIsLocal(pToUser)) {
                         curChannel->PublicSendBut(curUser->GetName(),NULL
                                                  ,AllText
                                                  ,RECV_TYPE_ACTION,ACTMSG_OUT
                                                  ,NULL,NULL,NULL
                                                  ,pToUser->GetName()
                                                  ,curUser->GetName());
                    }
                    actTmpTo=NULL;
                    actHandleResp(AF_STDDIR,ACTMSG_RES,pToUser
                                 ,tmpact->resp,AllText);
               }
          }
     }
     actTmpTo=NULL;
     actHasFile=FALSE;
     return(actresp);
}

static VOID
actHandleResp(                     // handle response to an action
INT DirType,                       //   direction type (normal/all)
SHORT RespType,                    //   response message type (normal/secret)
CTlcUser* pToUser,                 //   user at whom action is directed
CHAR* RespFormat,                  //   response format string
const CHAR* AllText,               //   text displayed to everyone else
const CHAR* arg)                   //   additional argument
{
     if (!isempty(RespFormat)) {
          AllText=actfmt(RespFormat,DirType,pToUser);
     }
     curUser->OnRecvText(SYSUID,NULL,AllText,RECV_TYPE_ACTION,RespType,arg);
}

GBOOL
actHasFromUser(                    /* does action contain @FU/P macro?     */
const CHAR* fmt)                   /*   action format string               */
{
     const CHAR* tmp;

     while (*fmt != '\0') {
          switch (*fmt) {
          case '@':
               ++fmt;
               switch (*fmt) {
               case 'A':
               case 'B':
               case 'T':
               case 'I':
                    ++fmt;
               case '@':
               case 'E':
               case 'N':
               case 'C':
               default:
                    ++fmt;
                    break;
               case '0':
               case '1':
               case '2':
               case '3':
               case '4':
               case '5':
               case '6':
               case '7':
               case '8':
               case '9':
                    {
                         INT i=1;
                         while (i < 3 && isdigit(fmt[i])) {
                              i++;
                         }
                         fmt+=i;
                    }
                    break;
               case '<':
                    ++fmt;
                    if ((tmp=strchr(fmt,'>')) != NULL) {
                         fmt=tmp+1;
                    }
                    break;
               case '(':
                    ++fmt;
                    if ((tmp=strchr(fmt,')')) != NULL) {
                         fmt=tmp+1;
                    }
                    break;
               case 'F':
                    ++fmt;
                    switch (*fmt) {
                    case 'P':
                    case 'U':
                         return(TRUE);
                    }
                    ++fmt;
                    break;
               }
               break;
          default:
               ++fmt;
               break;
          }
     }
     return(FALSE);
}

const CHAR *                       /*   returns pointer to internal buffer */
fmtsimpall(
LPACTION pAct,
const CHAR* user)
{
     CTlcUser* pUser=tlcAPI->usrGetByName(user);
     if (pUser == NULL) {
          return(NULL);
     }
     othusn=-1;
     curUser=pUser;
     return(actfmt(pAct->simple,AF_STDDIR,pUser));
}

const CHAR *                       /*   returns pointer to internal buffer */
fmtcomplxall(
LPACTION pAct,
const CHAR* user,
const CHAR* userto,
const CHAR* extra)
{
     const CHAR* mem;

     CTlcUser* pUser=tlcAPI->usrGetByName(user);
     if (pUser == NULL) {
          return(NULL);
     }
     curUser=pUser;

     strcpy(input,pAct->name);
     strcat(input," ");
     strcat(input,extra);
     parsin();

     CTlcUser* pTo=tlcAPI->usrGetByName(userto);

     if (pAct->isyell) {
          mem=actfmt(pAct->complx,AF_STDDIR,curUser);
     }
     else {
          if (NULSTR(userto) || sameas(userto,"all")) {
               mem=actfmt(pAct->complx,AF_DIRALL,pTo);
          }
          else {
               mem=actfmt(pAct->complx,AF_STDDIR,pTo);
          }
     }
     return(mem);
}

const CHAR *                       /*   returns pointer to internal buffer */
fmtcomplxto(
LPACTION pAct,
const CHAR* user,
const CHAR* userto)
{
     CTlcUser* pUser=tlcAPI->usrGetByName(user);
     if (pUser == NULL) {
          return(NULL);
     }
     curUser=pUser;
     CTlcUser* pTo=tlcAPI->usrGetByName(userto);
     return(actfmt(pAct->complx,AF_DIROTH,pTo));
}

VOID
actPrfFrame(                       /* frame an action in prfbuf            */
SHORT iMsgNum,                     /*   action type identifier             */
const CHAR* pMsg,                  /*   text associated with action        */
const CHAR* ptr1)                  /*   additional argument                */
{
     switch (iMsgNum) {
     case ACTMSG_OUT:         // public message to anyone
          prfmsg(ACTFRAE3,pMsg);
          break;
     case ACTMSG_OUT_SECRET:  // secret message to recipient
          prfmsg(ACTFRAS3,pMsg);
          break;
     case ACTMSG_RES:         // normal response to sender
          prfmsg(RESFRAM3,pMsg);
          break;
     case ACTMSG_RES_SECRET:  // response to sender for secret action
          prfmsg(RESFRAS3,pMsg,ptr1);
          break;
     }
}

CHAR*
FormatActionMsg(                   /* Sets the correct number of \1's      */
CHAR *aword,                       /*  Msg to be parsed of 1's and n's     */
GBOOL add1s)                       /*  add \1's                            */
{
     static CHAR awordF[RESSIZ];
     CHAR *ptr;
     INT Count;

     ASSERT(aword != NULL);

     setmem(awordF, RESSIZ, NULL);
     for (Count=0, ptr=aword; *ptr != '\0' && Count < RESSIZ; ptr++) {
          if (*ptr != '\n' && *ptr != '\1') {
               awordF[Count]=*ptr;
               Count++;
               if (Count % RESLIN == 0 && itemcntd(awordF, "\1") < 4
                && add1s) {
                    awordF[Count]='\1';
                    Count++;
               }
          }
          else if (Count > 0 && Count % RESLIN == 0
           && itemcntd(awordF, "\1") < 4 && add1s) {
               awordF[Count]='\1';
               Count++;
          }
     }
     if (add1s) {
          for (; itemcntd(awordF, "\1") < 4;) {
               strcat(awordF, "\1");
          }
     }
     return(awordF);
}

CHAR*
UnFormatActionMsg(                 /* Removes all \1's and \n's            */
CHAR *aword)                       /*  Msg to be parsed                    */
{
     return(FormatActionMsg(aword, FALSE));
}
