/*********************************************************************\
**                               ________________________________    **
**    A n t h o n y             |________    __    __    ________|   **
**                                       |  |o_|  |o_|  |            **
**            T h y s s e n            __|   __    __   |__          **
**                                  __|   __|  |  |  |__   |__       **
**   `` Dragon Computing ! ''    __|   __|     |  |     |__   |__    **
**                              |_____|        |__|        |_____|   **
**                                                                   **
\*********************************************************************/
/*   Parse the users configuration file to set up the extra workbench
** menus to execute various commands is various ways.
**                                             (See documentation)
*/
#undef DBUG   /* don't debug this unless nessary! */
#include "AMenu.h"
#include "AllocStr.h"        /* allocation/copy macro routines */


  /* Externs */
extern UBYTE    Color[], TColor[];
extern UWORD    FontWidth;
extern USHORT   ItemStyle;
extern unsigned MenuGap, WBMenus;

extern void  Warn(char *);
extern void  StartMenu(), AddTitle(char *), AddSkip();
extern struct ExtMenuItem   *AddItem(char *, char);
extern void  AddMenu(char *, int), AddSubMenu(char *, int);
extern void  EndMenu(), EndSubMenu();
extern BOOL  FinishMenu();


enum status {        /* Parse structure levels */
  STAT_ENTRY, STAT_MENU, STAT_SUB
};

  /* Multi character tokens */
enum {
  EOF = -1, STRING,
  CONSOLE, DIRECTORY, COLOR, TCOLOR, STYLE, COMP, BOX, MENUGAP,
  MENU, SUBMENU, ENDMENU, ENDSUBMENU, COLUMNS, ITEM, TITLE, SKIP,
  RB, RUNBACK, CLI, SCR, SCRIPT, WB,   STK, PRI, CON, DIR,   EndToken
};    /* Note command the modes `RB' to `WB' must be in together */

 /* The Arp keyword parsing string - in numeral order of tokens above */
char  *TokenTemplate =
 "CONSOLE,DIRECTORY,COLOR,TCOLOR,STYLE,COMP,BOX,MENUGAP,MENU,SUBMENU,ENDMENU,ENDSUBMENU,COLUMNS,ITEM,TITLE,SKIP,RB,RUNBACK,CLI,SCR,SCRIPT,WB,STK,PRI,CON,DIR";

 /* checks on the type of a commands argument */
#define isstring(t) ( (t)<EndToken )       /* is the token any string */
#define isnumber(t) ( isstring(t) && isdigit(*String) )


#define S_SIZE  80
char    String[S_SIZE], NextChar;   /* string token, char after string */
BPTR    Config;           /* File Handle */


/*   NOTE :- The Lattice 5.04 library function `isalnum()' is
** incorrect in that the characters  :;<=>?@ are true (is an
** alphanumeric character).  The library functions `isdigit()'
** and `isalpha()' is correct, and thus used instead.
**   The macro isalnum() in "ctype.h" works correctly, but
** involves the linking of the 256 byte flag buffer `_ctype[]'
** used by the macros.
*/
#define isalnum(c)  (isalpha(c) || isdigit(c))


/*--------------------------------------*/

static int LineNum = 0;      /* Line number in config file - 0 = No Need */

   /* return a True on any error given to the user */
#define ReportError(Msg)  { UserReport(Msg); return TRUE; }

static void
UserReport(Msg)
  char *Msg;
{
  if( LineNum )
    SPrintf(String, "%s : Line %ld \"%s\"", VERSION, LineNum, Msg);
  else
    SPrintf(String, "%s : \"%s\"", VERSION, Msg);
  Printf("\n%s\n", String);  /* CLI Report */
  Warn(String);              /* WB  Report */
}


static char
GetC()
{
 char c = EOF;
 Read(Config, &c, 1);
 if( c == '\n' )
   LineNum++;
 return c;
}


static char
GetToken()
{
  char quote;
  register char tok, *p;
  register int i;

retry:
  while(isspace(tok=GetC()));             /* skip extra spaces */
  if( tok == '#' ) {                         /* comment */
    while( (tok=GetC()) != '\n' && tok != EOF ) ;
    goto retry;
  }
  quote = (tok == '"');
  if( !quote && !isalnum(tok) )           /* individual Char */
    return tok;
  if( quote )                             /* remove open quote */
    tok=GetC();

    /* scan string token -- while !EOF, space in buffer */
  for( i=0, p=String;
         tok != EOF && i < (S_SIZE-1) && (quote ? tok!='"' : !isspace(tok));
           *p++=tok, i++, tok=GetC()  );
  *p = '\0';
  NextChar = tok;   /* record the next character after String */
    /* Use ARP to sort the keywords from the strings */
  tok = GetKeywordIndex( String, TokenTemplate ) + 1; /* convert token */
  DB( Printf( "%s ", String ); )
  return tok;
}



static BOOL
ParseConfig()
{
  register char       t, Statement;
  struct ExtMenuItem *EItem;
  char                Name[80], CommandChar, *p;
  BOOL                OptionLoop;
  enum status         status = STAT_ENTRY;
  int                 Columns;

  LineNum = 1;         /* report config line number on errors */
  DB( Printf("\n"); )
  while ( (Statement=GetToken()) != EOF ) {
    switch( Statement ) {
    case CONSOLE:
      if( !isstring(GetToken()) )
        ReportError("Bad CONSOLE Argument");
      if( M->Console )  FreeStr(M->Console);
      M->Console = AllocStr(String);
      break;
    case DIRECTORY:
      if( !isstring(GetToken()) )
        ReportError("Bad DIRECTORY Argument");
      if( M->Directory )  FreeStr(M->Directory);
      M->Directory = AllocStr(String);
      break;

    case COLOR:
      if( !isnumber(GetToken()) )
        ReportError("Bad COLOR Argument 1");
      Color[0] = (UWORD)Atol(String);
      if( !isnumber(GetToken()) )
        ReportError("Bad COLOR Argument 2");
      Color[1] = (UWORD)Atol(String);
      break;

    case TCOLOR:
      if( !isnumber(GetToken()) )
        ReportError("Bad TCOLOR Argument 1");
      TColor[0] = (UWORD)Atol(String);
      if( !isnumber(GetToken()) )
        ReportError("Bad TCOLOR Argument 2");
      TColor[1] = (UWORD)Atol(String);
      break;

    case STYLE:
      switch( GetToken() ) {
      case COMP: ItemStyle = HIGHCOMP; break;
      case BOX:  ItemStyle = HIGHBOX;  break;
      default:
        ReportError("Bad STYLE Argument");
      }
      break;

    case MENUGAP:
      if( !isnumber(GetToken()) )
        ReportError("Bad MENUGAP Argument");
      MenuGap = (UWORD)Atol(String);
      if( MenuGap<1 || MenuGap>50 )
        ReportError("MENUGAP out of range");
      MenuGap *= FontWidth;
      break;

    case MENU:
      switch( status ) {  /* fall through switch */
      case STAT_SUB:
        EndSubMenu();
      case STAT_MENU:
        EndMenu();
      }
      status = STAT_MENU;
      Columns = 1;
      if( (t=GetToken() == COLUMNS ) ) {
        if( !isnumber(t=GetToken()) )
          ReportError("Bad COLUMNS Argument");
        Columns = Atol(String);
        t=GetToken();
      }
      if( !isstring(t) )
        ReportError("Missing Menu Name");
      AddMenu( String, Columns );
      break;

    case SUBMENU:
      switch( status ) {
      case STAT_ENTRY:
        ReportError("Menu Statement expected");
      case STAT_SUB:
        EndSubMenu();
      }
      status = STAT_SUB;
      Columns = 1;
      if( (t=GetToken() == COLUMNS ) ) {
        if( !isnumber(t=GetToken()) )
          ReportError("Bad COLUMNS Argument");
        Columns = Atol(String);
        t=GetToken();
      }
      if( !isstring(t) )
        ReportError("Missing SubMenu Name");
      AddSubMenu( String, Columns );
      break;

    case ENDMENU:
      switch( status ) {
      case STAT_ENTRY:
        ReportError("Menu Statment expected");
      case STAT_SUB:
        EndSubMenu();
      }
      EndMenu();
      status = STAT_ENTRY;
      break;

    case ENDSUBMENU:
      if( status != STAT_SUB ) {
        ReportError("SubMenu Statment expected");
      }
      EndSubMenu();
      status = STAT_MENU;
      break;


    case SKIP:       /* these are pretty simalar */
    case TITLE:
    case ITEM:
      switch( status ) {
      case STAT_ENTRY:
        ReportError("Menu Statement expected");
      }
      if( Statement == SKIP ) {     /* if Skip finish up */
        AddSkip();
        break;
      }
      if( !isstring(GetToken()) )
        ReportError("Missing Item/Title Name");
      strcpy(Name, String);

      if( Statement == TITLE ) {    /* if Title finish up */
        AddTitle(Name);
        break;
      }
      CommandChar = '\0';
      if( (t=GetToken()) == '<' ) {     /* command char */
		/* the next line works with 1.3
		 * however, WB 2.0 has its own CTRL characters
		 * so simply get a token
        CommandChar = GetC();
		 */
		(void)GetC();	/* WB 2.0 "new" code */
        if( GetToken() != '>' )
          ReportError("Missing closing '>'");
        t = GetToken();
      }
      if( t != '|' )
        ReportError("Missing '|' separator");
      DB( Printf("| "); )
      EItem = AddItem(Name, CommandChar);
      if( (t=GetToken()) < RB || t > WB )
        ReportError("Missing Execution Mode");
      EItem->Mode  = Toupper(String[0]);   /* R,C,S,W as appropiate */
      EItem->Stack = DEF_STACK;            /* Default Stack Size */
      EItem->Pri   = 0;                    /* Default Prioity */
      for( OptionLoop=TRUE; OptionLoop; ) {
        switch( t=GetToken() ) {
        case STK:        /* Stack -- All modes */
          if( !isnumber(t=GetToken()) )
            ReportError("Bad STK Argument");
          EItem->Stack = (ULONG)Atol(String);
          if( EItem->Stack < 500 || EItem->Stack > 1000000 )
            ReportError("Bad STACK Size given");
          break;
        case PRI:        /* Prioity -- All modes */
          { BOOL neg;
            if( neg = ( (t=GetToken()) == '-') ) t=GetToken();
            if( !isnumber(t) )
              ReportError("Bad PRI Argument");
            EItem->Pri = (BYTE)Atol(String);
            if(neg) EItem->Pri = -EItem->Pri;
          }
          break;
        case CON:        /* Console -- CLI & SCRIPT only */
          if( EItem->Mode != 'C' && EItem->Mode != 'S' )
            ReportError("`CON' -- CLI or SCRIPT modes only");
          if( !isstring(t=GetToken()) )
            ReportError("Bad CON Argument");
          if( EItem->Console )  FreeStr(M->Console);
          EItem->Console = AllocStr(String);
          break;
        case DIR:        /* Current Directory -- not WB */
          if( EItem->Mode == 'W' )
            ReportError("`DIR' -- not in WB mode");
          if( !isstring(t=GetToken()) )
            ReportError("Bad DIR Argument");
          if( EItem->Directory )  FreeStr(M->Directory);
          EItem->Directory = AllocStr(String);
          break;
        default:
          OptionLoop = FALSE;
          break;
        } /* switch */
      } /* option loop */

        /* command */
      if( !isstring(t) )
        ReportError("Bad Command Given");
      EItem->Cmd = AllocStr(String);

        /* read arguments */
      t = NextChar;          /* char following command string*/
      while( t != '\n' && t != EOF && isspace(t) )
        t=GetC();            /* skip space */
      p = String;
      while( t != '\n' && t != EOF ) {
        *p++ = t;            /* read in rest of line */
        t = GetC(t);
      }
      *p++ = '\n';    /* make it a proper command line */
      *p = '\0';
      if( EItem->Mode != 'W' && EItem->Mode != 'S' ) /* ignore args? */
        EItem->Args = AllocStr(String);
      break; /* end of ITEM statement */

    default:
      ReportError("Keyword Expected");
    }

    DB( Printf("-END\n"); )
  } /* While Token */

  switch( status ) {
  case STAT_SUB:
    EndSubMenu();
  case STAT_MENU:
    EndMenu();
  }
  LineNum = 0;        /* finished config file - no more syntax errors */
  return FALSE;
}


BOOL
ParseMenus()
  /* Open and Read the Config File -- return FALSE if no error */
{
  BOOL Error;

  if( M->Directory )  FreeStr(M->Directory);
  M->Directory = AllocStr(DEF_DIRECTORY);
  if( M->Console )  FreeStr(M->Console);
  M->Console = AllocStr(DEF_CONSOLE);

  StartMenu();
  /*
   * WB 1.3 had only 3 menus in the work bench
  if( WBMenus != 3 )
   */
  if( WBMenus != 4 )
    ReportError("Unusual Workbench Menus Found!");

  Config = ArpOpen(AMENU_CONFIG, MODE_OLDFILE);
  if( !Config ) {
    Config = ArpOpen(AMENU_CONFIG2, MODE_OLDFILE);  /* closed by Arp */
    if( !Config )
      ReportError("Can't find file");
  }

  Error = ParseConfig();

  if( !Error && FinishMenu() ) {
    SPrintf(String, "%s : \"%s\"", VERSION,
              "Warning: Menu Overflows WorkBench Screen!");
    Warn(String);       /* Warning - don't abort */
  }

  return Error;
}

