#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <dos.h>
#include <exec/types.h>
#include <devices/tpt.h>
#include <dialog/resman.h>
#include <dialog/menu.h>
#include <dialog/user.h>
#include <dialog/cron.h>
#include <dialog/input.h>
#include <dialog/rxint.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/dlg.h>

#define COL (User->Ansi_Flag & ANSI_COLOR)

int    GetRexxCommand(char                *port,
                      struct USER_DATA    *user,
                      struct Ram_File     *ram,
		      char                *modname,
                      struct NewShortMenu *rlist,
                      UBYTE                numr,
                      int                  helplev,
                      int                (*callback)(UBYTE),
                      int                (*transcallback)(char *,char *),
	              char                *menuname
                     );

void   RexxResult    (struct RxQuery      *rxq,
                      struct MsgPort      *rxport,
                      int                  result
                     );

int    DoCommand     (char                *menuname,
                      char                *port,
                      char                *command,
                      char                *modname,
                      struct NewShortMenu *rlist,
                      UBYTE                numr,
                      int                (*callback)(UBYTE),
                      struct USER_DATA    *StUser,
                      struct Ram_File     *RStruct,
                      int                  helplev,
	              int                (*transcallback)(char *,char *)
                     );

int    Internal      (UBYTE                funcnum,
                      char                *port,
                      char                *modname,
                      struct NewShortMenu *rlist,
                      UBYTE                numr,
                      struct USER_DATA    *StUser,
                      struct Ram_File     *RStruct,
                      int                  helplev,
                      int                (*transcall)(char *,char *)
                     );

struct CompiledMenuEntry *CopyEntry  (struct CompiledMenuEntry *cme);
void                      FreeCME    (struct CompiledMenuEntry *cme);
struct CompiledMenuEntry *FindCommand(char command,struct MenuNode *mun);

int    DisplayMenu   (struct MenuStuff    *ms,
                      struct NewShortMenu *rlist,
                      UBYTE                numr,
                      struct USER_DATA    *User,
                      int                  helplev,
                      struct Ram_File     *ramfile,
                      char                *ext,
                      int                (*transcall)(char *,char *));

int    Visible       (struct CompiledMenuEntry *cme,
                      struct BatchEntry        *be,
                      struct NewShortMenu      *rlist,
                      UBYTE                     numr,
                      UBYTE                     level,
                      UBYTE                     exeflag,
                      UBYTE                     functype
                     );

long   Grab          (char *buf, char **str, char **left);


extern char            **SA;
extern struct Library   *DLGBase;

BPTR                menu_out;
struct Query        menuq = {"", NULL, NULL, NULL, NULL, 1, 0, QUERY_UPCASE|QUERY_NODEFEDT};
struct UserInfo     menuui;

#define NUMI 2
struct NewShortMenu ilist[] = {{"DisplayMenu",1},
                               {"Help",       1}
                              };

int MenuInput(char                *menuname,
              char                *port,
              char                *modname,
              struct NewShortMenu *rlist,
              UBYTE                numr,
              int                (*callback)(UBYTE),
              struct USER_DATA    *StUser,
	           struct Ram_File     *RStruct,
              int                  helplev,
	           int                (*transcallback)(char *,char *))

{struct MenuNode *mun;
 struct MenuStuff ms;

 short            retval;
 char             command[512];

 menu_out    = Output();
 menuui.User = StUser;
 menuui.Ram  = RStruct;

 if (!(RStruct->status & IN_AREXX))
    {ms.name    = menuname;
     ms.custnov = NULL;
     ms.custint = NULL;
     if (LockMenu(port,&ms,StUser->menuset,modname,"Displaying",0,PENDLOCK)!=RMNOERR)
        {AFPrintf(StUser, menu_out, SA[3527]);
         return(MENUNOTFOUND);
        }

     mun = ms.mun;
     if (StUser->Help_Level!=EXPERT && !*RStruct->Command_Stack)
        {retval = DisplayMenu(&ms,rlist,numr,StUser,helplev,RStruct,port,transcallback);

         RStruct->Command_Stack[1] = 0;
         if (retval == 13)
             RStruct->Command_Stack[0] = ';';
           else
             if (retval > 31)
                 RStruct->Command_Stack[0] = retval;
        }

     if (!*RStruct->Command_Stack)
        {HandleBCMsgs(port);

         if (*mun->prompt)
            {char  tempstr[512];

             TranslateBuffer(mun->prompt, command, 512, StUser, RStruct, port);
             if (transcallback)
                {TransBuf(command, tempstr, 512, transcallback);
                 strcpy(command, tempstr);
                }

             AFPrintf(StUser, menu_out, command);
            }
          else
            AFPrintf(StUser, menu_out, SA[3528]);
        }

     FreeMenu(port,NULL,modname);
    }

 if (*(RStruct->Command_Stack) == '\001')
    {long  count;
     long  funcnum;

     funcnum = atol(RStruct->Command_Stack+1);

     for(count = 1; isdigit(RStruct->Command_Stack[count]); count++);
     movmem(RStruct->Command_Stack+count+1,RStruct->Command_Stack,strlen(RStruct->Command_Stack)-count);

     return((*callback)((UBYTE)funcnum));
    }

 if (RStruct->chainflag)
    {if (!*RStruct->Action)
          ASPrintf(StUser,RStruct->Action,SA[3529],mun->mn.node.ln_Name);
        else
         {TranslateBuffer(RStruct->Action,command,80,StUser,RStruct,port);
          if (transcallback)
              TransBuf(command,RStruct->Action,80,transcallback);
            else
              strcpy(RStruct->Action,command); 
         }

     RStruct->chainflag = 0;
     WriteRam(RStruct, port);
    }

 if (RStruct->status & IN_AREXX)
    {retval = GetRexxCommand(port,StUser,RStruct,modname,rlist,numr,helplev,callback,transcallback,menuname);
     return(retval);
    }
  else
    {Chk_Abort();
     menuq.string     = command;
     menuq.typelength =   254;
     if (!DLGQuery(&menuq, &menuui))
        {command[0] = '\n';
         command[1] =   0;
        }

     Clr(StUser->Ansi_Flag);
     return(DoCommand(menuname,port,command,modname,rlist,numr,callback,StUser,RStruct,helplev,transcallback));
    }
}


int GetRexxCommand(char                *port,
                   struct USER_DATA    *user,
                   struct Ram_File     *ram,
		   char                *modname,
                   struct NewShortMenu *rlist,
                   UBYTE                numr,
                   int                  helplev,
		   int                (*callback)(UBYTE),
                   int                (*transcallback)(char *,char *),
		   char                *menuname)

{struct MsgPort *rxport;
 struct MsgPort *rport;

 struct RxQuery *rxm;
 struct RxQuery  rxq;

 char            tbuf [512];
 char            tbuf2[512];

 char            rxaddr[6];
 int             count;
 char           *ifunc = NULL;
 char           *rfunc = NULL;
 char           *cmd;
 char           *stk;
 UBYTE           funcnum;
 int             result = 0;

 ASPrintf(NULL, rxaddr, "%sRX", port);
 Upper(rxaddr);

 for(count = 0; count < 20; count++)
    {Forbid();
     rxport = FindPort(rxaddr);
     Permit();
     if (rxport) break;
     Delay(10);
    }

 if (!rxport)
    {AFPrintf(user, menu_out, SA[3530]);
     return(FALSE);
    }

 rport = CreateMsgPort();
 if (!rport)  return(FALSE);

 rxq.mess.mn_Node.ln_Type = NT_MESSAGE;
 rxq.mess.mn_ReplyPort    = rport;
 rxq.mess.mn_Length       = sizeof(struct RxQuery);
 rxq.user                 = user;
 rxq.ram                  = ram;
 rxq.rstr                 = NULL;
 PutMsg(rxport, (struct Message *)&rxq);

 while(!(rxm = (struct RxQuery *)GetMsg(rport)))
         WaitPort(rport);
 DeleteMsgPort(rport);

 if (!rxm->command)
    {result       =  0;
     ram->status &= ~IN_AREXX;

     if (*ram->DefAction)
        {char transbuf[80];

         TranslateBuffer(ram->DefAction, transbuf, 80, user, ram, port);
         if (transcallback)
             TransBuf(transbuf, ram->Action, 80, transcallback);
           else
             strcpy(ram->Action, transbuf);
        }
      else
        ASPrintf(user, ram->Action, SA[3529], menuname);

     WriteRam(ram, port);
     TSetFlags  (T_RAW,  port);
     TUnSetFlags(T_ECHO, port);
    }
  else
    {if (!Strnicmp(rxm->command,"CLI",3))
        {TUnSetFlags(T_RAW,  port);
         TSetFlags  (T_ECHO, port);
        }
      else
        if (!Strnicmp(rxm->command,"NOCLI",5))
           {TSetFlags  (T_RAW,  port);
            TUnSetFlags(T_ECHO, port);
           }
         else
           if (!Strnicmp(rxm->command,"OVERLAY",7))
              {WriteRam(ram, port);
               WriteUser(ram->Name, user);
      
               RexxResult(rxm, rxport, result);

               OverlayProgram(rxm->command+8);
               ReadUser(ram, user, port);
               return(result);
              }
            else
              if (!Strnicmp(rxm->command,"CHAIN",5))
                 {(*callback)(0);
                  RexxResult(rxm, rxport, result);
                  ChainProgram(rxm->command+6, port);
                  exit(0);
                 }
               else
                 if (!Strnicmp(rxm->command,"TRANSLATE",9))
                    {rxm->rstr = tbuf;
                     TranslateBuffer(rxm->command+10, tbuf, 512, user, ram, port);
                     if (transcallback)
                        {rxm->rstr = tbuf2;
                         TransBuf(tbuf, tbuf2, 512, transcallback);
                        }
                    }
                  else
                    {for(cmd = stk = rxm->command; *stk && (*stk!=' '); stk++);
                     if (*stk)  *stk++='\0';

                     if (!(ifunc = DLGBinSearch((char *)ilist,cmd,sizeof(struct NewShortMenu),21,NUMI))  &&
                         !(rfunc = DLGBinSearch((char *)rlist,cmd,sizeof(struct NewShortMenu),21,numr)))
                         AFPrintf(user, menu_out, SA[3531], cmd);
                       else
                        {if (*stk)  InsertStack(ram->Command_Stack,stk);

                         if (ifunc)
                            {funcnum = (UBYTE)(((long)ifunc-(long)ilist) / (long)sizeof(struct NewShortMenu));
                             result  =  Internal(funcnum,port,modname,rlist,numr,user,ram,helplev,transcallback);
                            }
                          else
                            {funcnum = (UBYTE)(((long)rfunc-(long)rlist) / (long)sizeof(struct NewShortMenu));
                             result  = (*callback)((UBYTE)funcnum);
                            }
                        }
                    }
    }

 if (ram->status & IN_AREXX)  RexxResult(rxm, rxport, result);
 return(result);
}


void RexxResult(struct RxQuery *rxq,
                struct MsgPort *rxport,
                int             result)

{struct MsgPort *rport;

 rport                  = CreateMsgPort();
 rxq->mess.mn_ReplyPort = rport;
 rxq->rcode             = result;

 PutMsg(rxport, (struct Message *)rxq);
 while(!(rxq = (struct RxQuery *)GetMsg(rport)))
         WaitPort(rport);
 DeleteMsgPort(rport);
}


int DoCommand(char                *menuname,
              char                *port,
              char                *command,
              char                *modname,
	      struct NewShortMenu *rlist,
              UBYTE                numr,
              int                (*callback)(UBYTE),
	      struct USER_DATA    *StUser,
              struct Ram_File     *RStruct,
              int                  helplev,
	      int                (*transcallback)(char *,char *))

{struct MenuNode          *mun;
 struct CompiledMenuEntry *cme;
 struct CompiledMenuEntry *tcme;
 struct BatchEntry        *be;
 struct BatchEntry        *ie;
 struct MenuStuff          ms;

 char                      transbuf[256];
 char                      buffer  [512];

 UBYTE                     priorityset   = FALSE;
 UBYTE                     stopflag      = FALSE;
 UBYTE                     abortflag;
 int                       priority;
 int                       clialreadyset = FALSE;
 int                       funcnum;
 int                       ifuncnum;

 ms.name    = menuname;
 ms.custnov = NULL;
 ms.custint = NULL;
 if (LockMenu(port, &ms, StUser->menuset, modname, "Executing", 0, PENDLOCK) != RMNOERR)
    {AFPrintf(StUser, menu_out, SA[3527]);
     RStruct->Command_Stack[0] = 0;
     return(FALSE);
    }

 mun  = ms.mun;
 tcme = FindCommand(*command, mun);

 if (tcme ||
    (isdigit(*command) && (tcme = FindCommand(mun->numentry,mun)) && InsertStack(RStruct->Command_Stack,command)))
    {if (!tcme->blist)
        {FreeMenu(port, NULL, modname);
         RStruct->Command_Stack[0] = 0;
         return(FALSE);
        }

     cme = CopyEntry(tcme);
     FreeMenu(port, NULL, modname);
     WriteRam(RStruct, port);

     if (cme->logvalue)  WriteLog(cme->logvalue, RStruct->Name, port, "");

     be = cme->blist;
     ie = cme->blist;

     while(!stopflag && be)
          {if (!(((funcnum=Visible(cme,be,rlist,numr,StUser->User_Level,1,1))!=-1) ||
               ((ifuncnum=Visible(cme,be,ilist,NUMI,StUser->User_Level,1,0))!=-1)))
              {be = be->next;
               continue;
              }

           if (be->priority!=-127)
              {priorityset = TRUE;
               priority    = SetTaskPri(FindTask(0), be->priority);
              }

           if ((be->flags & MENU_ASK) && (be==cme->blist))
              {if (*RStruct->Command_Stack=='\0')  AFPrintf(StUser, menu_out, SA[3532], cme->description);
               abortflag = BoolQuery("", 0, &menuui);
               if (!*RStruct->Command_Stack)       AFPrintf(StUser, menu_out, "\n\n");
	       if (!abortflag) break;
              }

           if (!(be->flags & MENU_BCPEND))
              {HandleBCMsgs(port);
               BCResume(port);
              }
            else
               BCPend(port);

           if (be->flags & MENU_CLI  &&  !clialreadyset)
              {clialreadyset = TRUE;

               if (be->type != MENU_BUILTIN  &&  be->type != MENU_STACK)
                  {TUnSetFlags(T_RAW,port);
                   TSetFlags(T_ECHO,port);
                  }
                else
                  {TSetFlags(T_RAW,port);
                   TUnSetFlags(T_ECHO,port);
                  }
              }

           if ((be->type == MENU_EXE  &&  be->flags & MENU_CHAIN) || be->type==MENU_SUB)
              {if (be->action)
                   strcpy(RStruct->DefAction, be->action);
                 else
                  {strcpy(RStruct->DefAction, "");
                   strcpy(RStruct->Action,    "");
                  }

               RStruct->chainflag = 1;
               WriteRam(RStruct,port);
              }

           if (be->action || *RStruct->DefAction)
              {UBYTE defflag=0;

               if (!be->action)
                  {be->action = RStruct->DefAction;
                   defflag    = 1;
                  }

               TranslateBuffer(be->action,transbuf,80,StUser,RStruct,port);
               if (transcallback)
                   TransBuf(transbuf,RStruct->Action,80,transcallback);
	         else
                   strcpy(RStruct->Action,transbuf); 

               if (defflag) be->action=NULL;

               WriteRam(RStruct,port);
              }

           if (be->program)
              {TranslateBuffer(be->program,transbuf,256L,StUser,RStruct,port);
               if (transcallback)
                  {TransBuf(transbuf,buffer,256,transcallback);
                   strcpy(transbuf,buffer);
                  }
              }


           switch(be->type)
                 { case MENU_EXE: if (!transbuf[0])  break;

                                  if (be->flags & MENU_CHAIN)
                                     {FreeCME(cme);
                                     (*callback)(0);
                                      ChainProgram(transbuf, port);
                                      CloseLibrary(DLGBase);
                                      exit(0);
                                     }
                                   else
                                     {/* Get rid of the DefAction while in the OVERLAYed module */
                                      strcpy(buffer, RStruct->DefAction);
                                      strcpy(RStruct->DefAction, "");
                                      WriteRam(RStruct, port);
                                      WriteUser(RStruct->Name, StUser);

                                      OverlayProgram(transbuf);
                                      ReadUser(RStruct,StUser,port);

                                      /* Replace the DefAction now that we have returned */
                                      strcpy(RStruct->DefAction,buffer);
                                     }
                                   break;

                  case MENU_BATCH: ASPrintf(StUser,buffer,"Execute %s",transbuf);

                                   if (be->flags & MENU_CHAIN)
                                      {(*callback)(0);
                                       stopflag = TRUE;
                                       Execute(buffer, 0, 0);
                                      }
                                    else
                                      {WriteUser(RStruct->Name, StUser);
                                       Execute(buffer, 0, 0);
                                       ReadUser(RStruct, StUser, port);
                                      }
                                   break;

                    case MENU_SUB: ms.name    = be->program;
                                   ms.custnov = NULL;
                                   ms.custint = NULL;

                                   if (LockMenu(port,&ms,StUser->menuset,modname,"Switching",0,PENDLOCK)!=RMNOERR)
                                       break;

                                   mun = ms.mun;
                                   FreeMenu(port,NULL,modname);
                                   strcpy(menuname,be->program);
                                   stopflag = TRUE;
                                   break;

                  case MENU_STACK: InsertStack(RStruct->Command_Stack,be->program);
                                   break;

                  case MENU_AREXX: if (transbuf[0])
                                      {ASPrintf(StUser, buffer, "DLGRx -p %s -s %s", port, transbuf);
                                       RStruct->status |= IN_AREXX;
                                       WriteRam (RStruct, port);
                                       WriteUser(RStruct->Name, StUser);
                                       CronEvent(ADDEVENT, 0, buffer);
                                       ReadUser (RStruct, StUser, port);
                                      }            
                                   break;

                case MENU_BUILTIN: if (funcnum!=-1)
                                      {if (!(*callback)((UBYTE)funcnum))
                                             *RStruct->Command_Stack='\0';
                                      }
                                    else
                                       Internal((UBYTE)ifuncnum,port,modname,rlist,numr,StUser,RStruct,helplev,transcallback);
                                   break;

                          default: AFPrintf(StUser, menu_out, SA[3533]);
                                   break;
                 }

           if (!stopflag && (be->flags & MENU_RETURN) && (!(RStruct->status & IN_AREXX)) && (StUser->Help_Level==NOVICE || *RStruct->Command_Stack))
              {TSetFlags(T_RAW,port);
               TUnSetFlags(T_ECHO,port);

               Pause();
               Clr(StUser->Ansi_Flag);
              }

           if (be->action && !(RStruct->status&IN_AREXX))
              {if (*RStruct->DefAction)
                  {TranslateBuffer(RStruct->DefAction, transbuf, 80, StUser, RStruct, port);
                   if (transcallback)
                       TransBuf(transbuf,RStruct->Action,80,transcallback);
                     else
                       strcpy(RStruct->Action,transbuf);
                  }
                else
                  ASPrintf(StUser,RStruct->Action,SA[3529],mun->mn.node.ln_Name);

               WriteRam(RStruct,port);
              }

           if (priorityset)  SetTaskPri(FindTask(0), priority);

           be = be->next;
           if (be)
              {if (!be->action)
                    be->action = ie->action;
                  else
                    ie->action = be->action;
              }

          }

     if (funcnum==-1 && ifuncnum==-1)
        *RStruct->Command_Stack='\0';

     if (!(RStruct->status & IN_AREXX))
        {TSetFlags(T_RAW,port);
         TUnSetFlags(T_ECHO,port);
        }

     BCPend(port);
     FreeCME(cme);
    }
  else
    {RStruct->Command_Stack[0] = '\0';
     FreeMenu(port,NULL,modname);
    }
}


int Internal(UBYTE                funcnum,
             char                *port,
             char                *modname,
             struct NewShortMenu *rlist,
	     UBYTE                numr,
             struct USER_DATA    *StUser,
             struct Ram_File     *RStruct,
	     int                  helplev,
             int                (*transcall)(char *,char *))

{struct MenuNode          *mun;
 struct CompiledMenuEntry *cme;
 struct MenuStuff          ms;
 int    retval = FALSE;
 char   buf[80];
 char   command[2];

  switch(funcnum)
        {case 0:
         case 1: if (helplev==NOVICE && !funcnum) break;

                 ms.name    = NULL;
                 ms.custnov = NULL;
                 ms.custint = NULL;
                 if (LockMenu(port,&ms,StUser->menuset,modname,"Getting help",0,PENDLOCK)!=RMNOERR)
                    {AFPrintf(StUser, menu_out, SA[3527]);
                     retval = FALSE;
                     break;
                    }

                 mun = ms.mun;
                 if (!*RStruct->Command_Stack)
                    {retval = DisplayMenu(&ms,rlist,numr,StUser,NOVICE,RStruct,port,transcall);

                     RStruct->Command_Stack[1] = 0;
                     if (retval == 13)
                         RStruct->Command_Stack[0] = ';';
                       else
                         if (retval > 31)
                             RStruct->Command_Stack[0] = retval;
                    }

                 FreeMenu(port, NULL, modname);

                 if (!funcnum)
                    {retval=TRUE;
                     break;
                    }
 
                 if (!*RStruct->Command_Stack)
                      AFPrintf(StUser, menu_out, SA[3534]);

                 Chk_Abort();
                 menuq.string     = command;
                 menuq.typelength =    1;
                 if (!DLGQuery(&menuq, &menuui))
                    {AFPrintf(StUser, menu_out, SA[3535]);
                     break;
                    }

                 Clr(StUser->Ansi_Flag);

                 ms.name    = NULL;
                 ms.custnov = NULL;
                 ms.custint = NULL;
                 if (LockMenu(port,&ms,StUser->menuset,modname,"Getting Help",0,PENDLOCK)!=RMNOERR)
                    {AFPrintf(StUser, menu_out, SA[3527]);
                     retval = FALSE;
                     break;
                    }

                 mun = ms.mun;
                 if (!(cme=(struct CompiledMenuEntry *)FindCommand(*command,mun))    ||
                    ((Visible(cme,cme->blist,rlist,numr,StUser->User_Level,1,1)==-1) &&
                    (Visible(cme,cme->blist,ilist,NUMI,StUser->User_Level,1,0)==-1)))
                    {FreeMenu(port,NULL,modname);
                     AFPrintf(StUser, menu_out, SA[3536]);
                     break;
                    }

                 FreeMenu(port,NULL,modname);
                 if (cme->helpfile)  ASPrintf(NULL, buf, "DLGCONFIG:menu/%s.help", cme->helpfile);
                 if (!cme->helpfile || !Exists(buf))
                    {AFPrintf(StUser, menu_out, SA[3537]);
                     break;
                    }

                 DispForm(buf, StUser, StUser, RStruct, port);
                 AFPrintf(StUser, menu_out, "\n\n");
                 retval = TRUE;
                 break;

        default: retval=FALSE;
                 break;
        }

 return(retval);
}


struct CompiledMenuEntry *CopyEntry(struct CompiledMenuEntry *cme)

{struct CompiledMenuEntry *newcme;
 struct BatchEntry        *sbe;
 struct BatchEntry        *be;
 struct BatchEntry        *lastbe = NULL;

 long size1 = 0;
 long size2 = 0;

 if (cme->description)  size1 = strlen(cme->description);
 if (cme->helpfile)     size2 = strlen(cme->helpfile);

 if (!(newcme = malloc(sizeof(struct CompiledMenuEntry)+size1+size2+2)))
       return(NULL);
 movmem(cme, newcme, sizeof(struct CompiledMenuEntry));

 newcme->description = NULL;
 if (size1)
    {newcme->description = (char *)newcme + sizeof(struct CompiledMenuEntry);
     strcpy(newcme->description, cme->description);
    }

 newcme->helpfile    = NULL;
 if (size2)
    {newcme->helpfile = (char *)newcme + sizeof(struct CompiledMenuEntry) + size1 + 1;
     strcpy(newcme->helpfile, cme->helpfile);
    }

 newcme->blist = NULL;
 sbe           = cme->blist;

 while(sbe)
      {size1 = 0;
       size2 = 0;
       if (sbe->action)   size1 = strlen(sbe->action);
       if (sbe->program)  size2 = strlen(sbe->program);

       if (!(be = malloc(sizeof(struct BatchEntry)+size1+size2+2)))
             break;
       movmem(sbe, be, sizeof(struct BatchEntry));

       be->next   = NULL;

       be->action = NULL;
       if (size1)
          {be->action = (char *)be + sizeof(struct BatchEntry);
           strcpy(be->action, sbe->action);
          }

       be->program = NULL;
       if (size2)
          {be->program = (char *)be + sizeof(struct BatchEntry) + size1 + 1;
           strcpy(be->program, sbe->program);
          }

       if (lastbe)
           lastbe->next  = be;
         else
           newcme->blist = be;
       lastbe = be;
       sbe    = sbe->next;
      }

 return(newcme);
}


void FreeCME(struct CompiledMenuEntry *cme)

{struct BatchEntry *tbe;
 struct BatchEntry *sbe;

 sbe = cme->blist;

 while(sbe)
      {tbe = sbe;
       sbe = sbe->next;
       free(tbe);
      }

 free(cme);
}


struct CompiledMenuEntry *FindCommand(char command, struct MenuNode *mun)

{struct CompiledMenuEntry *cme     = mun->entries;
 long                      counter = mun->numentries;

 for(; counter; cme++, counter--)
     if (cme->command == command)  return(cme);

 return(FALSE);
}


int DisplayMenu(struct MenuStuff    *ms,
                struct NewShortMenu *rlist,
                UBYTE                numr,
		struct USER_DATA    *User,
                int                  helplev,
                struct Ram_File     *ramfile,
		char                *ext,
                int                (*transcall)(char *,char *))

{struct MenuNode          *mun;
 struct CompiledMenuEntry *cme;

 char   breakbuf[256];
 char  *bindex = breakbuf;

 UBYTE  *buf;
 UBYTE *custom = 0;
 UBYTE *outbuf;
 UBYTE *obuffer;

 long   size;
 long   len;

 menu_out = Output();
 mun      = ms->mun;
 AFPrintf(User, menu_out, "%ao");

 if (helplev == INTERMEDIATE)  custom = ms->custint;
 if (helplev == NOVICE)        custom = ms->custnov;

 if (custom)
    {char  *ifstring;
     char  *elsestring;
     char  *left;
     char  *str;

     size = (strlen(custom) * 2) + 255;

     if (buf = malloc(size*2))
        {outbuf = buf + size;

         TranslateBuffer(custom, buf, size, User, ramfile, ext);
         custom = buf;
         if (transcall)
            {TransBuf(buf, outbuf, size, transcall);
             custom = outbuf;
            }

        *bindex++ = 3;
         for(obuffer = outbuf; *custom;)
            {if (*custom != '%')
                {*obuffer++ = *custom++;
                  continue;
                }

             custom++;
             if (*custom < 33  ||  *custom > 126  ||  !(cme = FindCommand((*custom > 96 && *custom < 123)?(*custom & 223):*custom, mun)))
                {*obuffer++ = '%';
                  continue;
                }

             if ((len=Grab(custom+1, &ifstring, &left))  &&  (size=Grab(left, &elsestring, &left)))
                {str = elsestring;

                 if (!(cme->visibility & MENU_HIDDEN)  && ((User->User_Level >= cme->blist->llev  &&  User->User_Level <= cme->blist->ulev) || User->User_Level == 255))
                    {str = ifstring;

                     if (rlist)
                         if (cme->blist->type == MENU_BUILTIN)
                             if (!(rlist+(cme->blist->function))->status)  str = elsestring;
                    }

                 if (str == elsestring)  len = size;
                 len--;
                 movmem(str, obuffer, len);
                *bindex++ = cme->command;

                 obuffer += len;
                 custom   = left;
                }
              else
                {*obuffer++ = '%';
                  continue;
                }
            }
        }
    }
  else
    {long   counter;
     USHORT col = 0;
     USHORT cpc;
     USHORT cpl;

     cpc = (80 / mun->columns);
     if (cpc < 10)  cpc = 10;
     cpl =  User->Screen_Width / cpc;
     if (!cpl)  cpl = 1;
     len = (cpc * cpl) - 1;

     if (helplev == NOVICE  ||  helplev == 3)
        {size = ((cpc+10) * mun->numentries) + (mun->numentries / cpl) + 255;
         if (mun->name)  size += strlen(mun->name) + (len*2);

         *bindex++ = 3;
        }
      else
         size = (2 * mun->numentries) + 255;

     if (!(buf = malloc(size)))  return(FALSE);
     outbuf  = buf;
     obuffer = buf;

     if (mun->name  &&  (helplev==NOVICE || helplev==3))
        {obuffer += ASPrintf(User, obuffer, "%a2%s%a7\n", mun->name);

         SDraw_Line(obuffer, len);
         obuffer += len;
         obuffer += ASPrintf(User, obuffer, "%a2");
        }
      else
        {if (helplev == INTERMEDIATE)
             obuffer += ASPrintf(User, obuffer, "%a2(%a7");
           else
             obuffer += ASPrintf(User, obuffer, "%a2");
        }

     for(counter = 0; counter < mun->numentries; counter++)
        {cme = (mun->entries)+counter;
         if (!cme->blist)  continue;

         if (helplev != 3)
             if (Visible(cme,cme->blist,rlist,numr,User->User_Level,0,1)==-1 && Visible(cme,cme->blist,ilist,NUMI,User->User_Level,0,0)==-1)
                 continue;

         if (helplev == NOVICE  ||  helplev == 3)
            {if (cme->command == 10)
                 obuffer += ASPrintf(User, obuffer, "[%a7RET%a2] %-*.*s", cpc-8, cpc-8, cme->description);
               else
                 obuffer += ASPrintf(User, obuffer, "[%a7%c%a2] %-*.*s",  cme->command, cpc-6,cpc-6,cme->description);
             *bindex++ = cme->command;
      
             col++;
             if (col == cpl)
                {*obuffer++ = '\n';
                  col       =   0;
                }
              else
                {*obuffer++ = ' ';
                 *obuffer++ = ' ';
                }
            }
          else
            {if (counter)  *obuffer++ = ',';

             if (cme->command == 10)
                {strcpy(obuffer, "RET");
                 obuffer += 3;
                }
              else
                {*obuffer = cme->command;
                 obuffer++;
                }
            }
        }

     if (helplev == INTERMEDIATE)
         obuffer += ASPrintf(User, obuffer, "%a2)\n");
      else
        {if (col)  *obuffer++ = '\n';
         if (mun->name)
            {obuffer += ASPrintf(User, obuffer, "%a7");
             SDraw_Line(obuffer, len);
             obuffer += len;
            }
        }

    *obuffer++ = '\n';
    }


 {long   retval;
  USHORT pos  =        0;
  UBYTE  ansi = User->Ansi_Flag;

 *obuffer = 0;
 *bindex  = 0;

  if (custom)       User->Ansi_Flag &= ~(ANSI_POS);
  retval          = DispBuffer(menu_out, outbuf, &pos, 0, NULL, 0, breakbuf, User);
  User->Ansi_Flag = ansi;

  AFPrintf(User, menu_out, "%ao%a7");
  free(buf);
  return(retval);
 }
}


int Visible(struct CompiledMenuEntry *cme,
            struct BatchEntry        *be,
	    struct NewShortMenu      *rlist,
            UBYTE                     numr,
            UBYTE                     level,
            UBYTE                     exeflag,
	    UBYTE                     functype)

{struct NewShortMenu *temp;

 if (((cme->visibility & MENU_HIDDEN) && !exeflag) || (((level<be->llev) || (level>be->ulev)) && (level!=255)))
       return(-1);

 if (be->type!=MENU_BUILTIN)                          return(TRUE);
 if ((be->functype!=functype) || (be->function==-1))  return(-1);
 
 temp = (struct NewShortMenu *)rlist+be->function;
 if (temp->status)  return(be->function);
  
 return(-1);
}


long Grab(char *buf, char **str, char **left)

{int parencount=1;

 if (*(buf++)=='{')
    {*str = buf;

     for(;parencount && *buf; buf++)
        {if (*buf=='{') parencount++;
         if (*buf=='}') parencount--;
        }

     if (!parencount)
        {*left = buf;
          return(buf - *str);
        }
    }

 return(FALSE);
}
