/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* |_o_o|\\ Copyright (c) 1986 The Software Distillery.  All Rights Reserved */
/* |. o.| || This program may not be distributed without the permission of   */
/* | .  | || the authors.                                                    */
/* | o  | ||    Dave Baker     Ed Burnette  Stan Chow    Jay Denebeim        */
/* |  . |//     Gordon Keener  Jack Rouse   John Toebes  Doug Walker         */
/* ======          BBS:(919)-471-6436      VOICE:(919)-469-4210              */ 
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
 * VERY loosely based on the input.device example by Rob Peck, 12/1/85
 */

#include "popcli.h"
#include "popbug.h"

/* KLUDGE: (sort of) These strings can ONLY be accessed if gptr->stdout */
/*         is non-zero, since they *should* be in the PopCLI hunk that  */
/*         gets thrown away.                                            */
#define CSI "\x9B"
#define HILIT CSI "0;33m"
#define BOLD  CSI "1m"
#define PLAIN CSI "0m"
#define COPYR "\xA9"

char	killmsg[] =
   HILIT "PopCLI IV" PLAIN " Terminating\n";

char	banner[] =
   HILIT "PopCLI IV" PLAIN
   " - Copyright " COPYR " 1987,1989 The Software Distillery\n"
   " 235 Trillingham Lane, Cary NC 27513   BBS:(919)-471-6436\n";

char	helpmsg[] =
   "Usage:\n"
   "  " BOLD "POPCLI" PLAIN " [[secs | ADD | REPL] [key]] [[cmdmods] [CMD] cmnd]\n"
   "  " BOLD "POPCLI" PLAIN " FREE [key]\n"
   "  " BOLD "POPCLI" PLAIN " EXEC [key]\n"
   "  " BOLD "POPCLI" PLAIN " BLANK\n"
   "  " BOLD "POPCLI" PLAIN " UNBLANK\n"
   "  " BOLD "POPCLI" PLAIN " TIME n\n"
   "  " BOLD "POPCLI" PLAIN " QUIT\n"
   "  cmdmods: [NOWB] [MINMEM n] [MINCHIP n]\n"
   "where:\n"
   " secs    is the number of seconds before blanking screen\n"
   " key     is the scan code for the key to press with Left Amiga\n"
   "         <decimal>, 0x<hex>, F{1-10}, DEL, ESC, or 'c',\n"
   "         where c is any ASCII character from ' ' to '~'\n"
   " ADD     Activates Left Amiga-<key> without altering the blanking time\n"
   " REPL    Same as ADD, but more mnemonic for redefining a key\n"
   " NOWB    don't bring the workbench screen to front before executing command\n"
   " MINMEM  Make sure at least n bytes are available before executing command\n"
   " MINCHIP Like MINMEM, but checks for chip memory only.\n"
   " CMD     Optional tag, in case command starts with a keyword or number\n"
   " cmnd    is to be executed when Left Amiga-<key> is pressed\n"
   " FREE    Deactivates Left Amiga-<key>, but leaves POPCLI up\n"
   " EXEC    Executes the given key\n"
   " BLANK   Blanks the screen immediately\n"
   " UNBLANK Unblanks the screen, resetting the timer countdown\n"
   " TIME    Sets the timeout value without clobbering any key definitions\n"
   " QUIT    Brings down POPCLI and all command keys.\n"
   "\n"
   "From ARexx: ADDRESS POPCLI4 <popcli-arguments>\n";

/* Make this a multiple of 2 bytes long!!! */
struct KEYWORD {
	char	name[8];
	char	command;
	char	wherenum;
};

#define KW_QUIT 0
#define KW_NOWB	1
#define KW_CMD	2
#define KW_ESC  3
#define KW_DEL  4
#define KW_ADD	5
#define KW_REPL 6
#define KW_FREE	7
#define KW_TIME	8
#define KW_MEM	9
#define KW_CHIP	10
#define KW_EXEC 11
#define KW_BLNK 12
#define KW_UNBL 13

#define NUM_NONE 0
#define NUM_KEY	1
#define NUM_TIME 2
#define NUM_MEM -1
#define NUM_CHIP -2

#define ISMEMNUM(n)	(n < 0)

#ifdef CONST_KLUDGE
#define	PARSETBL_KLUDGE					\
	"quit\0\0\0\0"	"\04"		"\01"		\
	"nowb\0\0\0\0"	"\00"		"\01"		\
	"cmd\0\0\0\0\0"	"\00"		"\01"		\
	"esc\0\0\0\0\0"	"\00"		"\01"		\
	"del\0\0\0\0\0"	"\00"		"\01"		\
	"add\0\0\0\0\0"	"\01"		"\01"		\
	"repl\0\0\0\0"	"\01"		"\01"		\
	"free\0\0\0\0"	"\02"		"\01"		\
	"time\0\0\0\0"	"\07"		"\02"		\
	"minmem\0\0"	"\00"		"\xff"		\
	"minchip\0"	"\00"		"\xfe"		\
	"exec\0\0\0\0"	"\03"		"\01"		\
	"blank\0\0\0"	"\05"		"\01"		\
	"unblank\0"	"\06"		"\01"		\
        "\00"   /* To ensure even alignment */

#define KW_MAX 14


#else
const struct KEYWORD parsetbl[] = {
	{ "quit",	MSG_SHUTDOWN,	NUM_KEY  },
	{ "nowb",	MSG_NONE,	NUM_KEY  },
	{ "cmd",	MSG_NONE,	NUM_KEY  },
	{ "esc",	MSG_NONE,	NUM_KEY  },
	{ "del",	MSG_NONE,	NUM_KEY  },
	{ "add",	MSG_ADDKEY,	NUM_KEY  },
	{ "repl",	MSG_ADDKEY,	NUM_KEY  },
	{ "free",	MSG_DELKEY,	NUM_KEY  },
	{ "time",	MSG_SETTIME,	NUM_TIME },
	{ "minmem",	MSG_NONE,	NUM_MEM  },
	{ "minchip",	MSG_NONE,	NUM_CHIP },
	{ "exec",	MSG_EXECUTE,	NUM_KEY  },
	{ "blank",	MSG_BLANK,	NUM_KEY  },
	{ "unblank",	MSG_UNBLANK,	NUM_KEY  }
};
#define KW_MAX ((sizeof(parsetbl))/(sizeof(struct KEYWORD)))
#endif

#define MAXKWLEN 8

#define KWCMP(a,b)	((*(long *)a)   == (*(long *)b) && \
			 ((long *)a)[1] == ((long *)b)[1])

/* SCODE table characters are in the following order: */

/*	 !"#$%&'()*+,-./          */
/*      @ABCDEFGHIJKLMNO          */
/*      PQRSTUVWXYZ[\]^_          */
/*      `abcdefghijklmno          */
/*      pqrstuvwxyz{|}~           */

#define SCODE                   \
    "\x40\x01\x2a\x03\x04\x05\x07\x2a\x09\x0a\x08\x0c\x38\x0b\x39\x3a"   \
    "\x0a\x01\x02\x03\x04\x05\x06\x07\x08\x09\x29\x29\x38\x0b\x39\x3a"   \
    "\x02\x20\x35\x33\x22\x12\x23\x24\x25\x17\x26\x27\x28\x37\x36\x18"   \
    "\x19\x10\x13\x21\x14\x16\x34\x11\x32\x15\x31\x1a\x0d\x1b\x06\x0a"   \
    "\x00\x20\x35\x33\x22\x12\x23\x24\x25\x17\x26\x27\x28\x37\x36\x18"   \
    "\x19\x10\x13\x21\x14\x16\x34\x11\x32\x15\x31\x1a\x0d\x1b\x00\x46"

static void WriteOut(  GLOBAL_DATA *, char *, int );

void popbanner( gptr )
GLOBAL_DATA	*gptr;
{
	WriteOut( gptr, banner, sizeof(banner)-1 );
}

void poptrailer( gptr )
GLOBAL_DATA	*gptr;
{
	WriteOut( gptr, killmsg, sizeof(killmsg)-1 );
}

int popparse( gptr, cmd, msg )
GLOBAL_DATA                   *gptr;
register char                 *cmd;
register struct OURMSG        *msg;
{
   long	buff[MAXKWLEN/sizeof(long)+1];
   char *p, *q;
   int  kw;
   int  len;
   int  value;
   char wherenum;
#ifdef CONST_KLUDGE
   struct KEYWORD *parsetbl = (struct KEYWORD *)PARSETBL_KLUDGE;
#endif

   msg->type = MSG_ADDKEY;
   msg->interval = 0;
   msg->key = DEFKEY;
   msg->minmem = 0;
   msg->minchip = 0;
   strcpy( msg->command, DEFCMD );
   msg->cmdflags = 0;

   wherenum = NUM_TIME;

   /* if we were run from CLI then output our banner and process parameters */
   if (!cmd)
      return( OK );

   if (*cmd == '?')
   {
      WriteOut(gptr, helpmsg, sizeof(helpmsg)-1);

      return( LEAVE );
   }

   while( *cmd )
   {
      while (*cmd && *cmd <= ' ') cmd++;

      /* Fetch the next keyword into a temp. buffer, lowercasing */
      buff[0] = buff[1] = 0;

      for (p = cmd, q = (char *)buff; *p > ' '; *q++ = (*p++ | ' '))
      {
         /* Too long to be a keyword... */
         if (q >= ((char *)buff)+MAXKWLEN)
            goto gotacmd;
      }

      for (kw = 0; kw < KW_MAX; kw++)
      {
         if (KWCMP(parsetbl[kw].name, buff))
         {
            /* We found a valid keyword! */

            wherenum = parsetbl[kw].wherenum;
            if (parsetbl[kw].command != MSG_NONE)
               msg->type = parsetbl[kw].command;

            switch( kw ) {
               case KW_QUIT:
                  return(SHUTDOWN);
                  break;

               case KW_NOWB:
                  msg->cmdflags |= FLAG_NOWB2F;
                  break;

               case KW_CMD:
                  /* Skip intervening blanks, then treat as a command */
                  for (cmd = p; *cmd && *cmd == ' '; cmd++);

                  goto gotacmd;
                  break;

               case KW_ESC:
                  msg->key = 0x45;
                  break;

               case KW_DEL:
                  msg->key = 0x46;
                  break;

               /* These are all handled by the table code!! */
               /* NOTE: I have arranged the parse table such that all */
               /* of these have larger values than the keywords which */
               /* actually *do* something here. I have to comment out */
               /* the case labels, though, because the compiler would */
               /* put them in the jump table anyway. */
            /* case KW_ADD:  */
            /* case KW_REPL: */
            /* case KW_FREE: */
            /* case KW_TIME: */
            /* case KW_MEM:  */
            /* case KW_CHIP: */
            /* case KW_EXEC: */
            /* case KW_BLNK: */
            /* case KW_UNBL: */
               default:
                  break;
            }

            goto kwdone;
         }
      }

      /* No keywords found; must be a key, number, or command */

      if (*((char *)buff) == 'f')
      {
         /* User possibly typed F1-F10, but if not, we don't */
         /* want to complain, just get the command           */
         len = stcd_i( ((char *)buff)+1, &value ) + 1;

         if (((char *)buff)[len] || (value < 1) || (value > 10))
            /* Treat it as a command */
            goto gotacmd;
         else
         {
            wherenum = NUM_KEY;
            value += 0x4f; /* Translate to scan code */
         }
      }
      else if (*((short *)buff) == 0x3078)   /* "0x" */
      {
         /* Number is in hex, rather than decimal */
         len = stch_i( ((char *)buff)+2, &value ) + 2;
      }
      else if (*((char *)buff) == '\'')  /* ' */
      {
         /* Number is an actual key picture */
         value = SCODE[(((char *)buff)[1] & 0x7f)-' '];
         len = 3;
      }
      else
         len = stcd_i( (char *)buff, &value );

      /* Make sure we got a valid value. If not, must be a */
      /* start of a command.                               */
      if (((char *)buff)[len])
      {
         goto gotacmd;  /* Leaves the function */
      }

      if (value && ISMEMNUM(wherenum))
      {
         value = ((value >> 10) + 1);
         if (wherenum == NUM_MEM)
            msg->minmem = value;
         else
            msg->minchip = value;
      }
      else if (wherenum == NUM_TIME)
         msg->interval = value;
      else
         msg->key = value;

kwdone:
      cmd = p;
   }

gotacmd:
   if (*cmd)
   {
      strncpy( msg->command, cmd, MAXCMD-1 );
      msg->command[MAXCMD-1] = '\0';
   }

   return(OK);
}

static void WriteOut( gptr, msg, len )
GLOBAL_DATA	*gptr;
char		*msg;
int		len;
{
	if (gptr->stdout)
		Write( gptr->stdout, msg, len );
}

