/* Remove the next line if not using the Aztec C compiler */
#define  AZTEC 1        /* defined if using Aztec C compiler */

#ifdef   AZTEC
#define  stpchr(s,c)  index(s,c)
#define  ABS(val) (((val) < 0) ? (-(val)) : (val))
#else
extern UBYTE *stpchr();
#endif

#include "exec/types.h"
#include "exec/exec.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"
#include <stdio.h>
#include <fcntl.h>

#define  WriteWork() Write(mycli_outfp, work, (long)strlen(work))

#ifdef   TRUE
#undef   TRUE        /* MANX defines these in an unusual way */
#undef   FALSE
#endif

#define  TRUE  1
#define  FALSE 0

/*
 * External functions
 */
extern   UBYTE    *stpblk();
extern   UBYTE    *AllocMem();
extern   struct   MsgPort     *CreatePort();
extern   struct   FileLock    *CreateDir();
extern   struct   FileLock    *CurrentDir();
extern   struct   FileLock    *Lock();
extern   struct   FileHandle  *Open();
extern   long     Seek(), Read(), Write(), ExNext();
extern   void     Close(), FreeMem(), DateStamp(), UnLock();

/*
 * forward references
 */
int   define_function_key(), endcli(), ;

/*
 * Tables
 */
struct {
   UBYTE  *cmdname;
   int   (*cmdfunc)();
   } command_table[] = {
   (UBYTE *)"def", &define_function_key,
   (UBYTE *)"endcli", &endcli,
   NULL, NULL
   };

UBYTE  *function_key_definitions[20] = {
   0,0,0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0,0,0
   };

/*
 * Globals
 */

UBYTE     work[514];
UBYTE     work2[514];
UBYTE     buf[514];
/*
 * Cli globals
 */
struct   FileHandle     *mycli_infp;      /* all i/o goes thru these two */
struct   FileHandle     *mycli_outfp;

UBYTE       chain_string[74];       /* holds character to chain commands */
UBYTE       path_string[74];        /* holds string for search path */
UBYTE       cmdbuf[15][74];         /* queue of last 16 commands */
UBYTE       cmd_string[74];         /* holds string typed in */
WORD        currcmd = 0;            /* current command for queue */

main()
{
UBYTE *nextptr;
UBYTE *ptr;
WORD  i;

sprintf(work, "Raw:0/0/640/100/");

mycli_infp = Open(work, MODE_NEWFILE);

if (mycli_infp == 0)
   exit(1L);

mycli_outfp = mycli_infp;

for ( i = 0; i < 16; i++)
   cmdbuf[ i ][ 0 ] = '\0';

/* Main program loop */

while (TRUE)
   {
   showprompt();                       /* show the custom prompt */
   getcommand( cmd_string );           /* get a command from the user */

   nextptr  = &cmd_string[ 0 ];        /* execute chained commands */
   while ( *nextptr )
      {
      for ( ptr = nextptr; *ptr && *ptr != chain_string[ 0 ]; ptr++ )
         ;
      if ( *ptr )
         *ptr++ = '\0';
      strcpy( buf, nextptr );
      CommandInterpreter( buf );       /* shuck off command to execute */
      nextptr = ptr;
      }                                /* end of while their is a string */
   }

}                                      /* end of function main() */

/* This function is like a read of the keyboard except
 * that if no key is pressed it immediately returns a zero
 */
UBYTE  scr_csts()
{
UBYTE inbuf;

if (WaitForChar(mycli_infp, 1L) == 0)
   return (0);
Read(mycli_infp, &inbuf, 1L);
return( inbuf );
}

CommandInterpreter( command )
UBYTE  *command;
{
struct FileHandle *oldfile;
struct FileHandle *tempfh;
short int i;
short int found;
UBYTE *ptr;
UBYTE *pc;
UBYTE *name;

/*
 * Scan through the command table for the string and invoke the function
 *    to do the actual work of the command.  Each of these commands is
 *    defined below, and the functions each take a pointer to the
 *    string containing the arguments passed the command line.
 */

command = stpblk(command);

found = FALSE;
oldfile = NULL;

for ( ptr = command; *ptr && *ptr != '>' && *ptr != '<'; ptr++ )
   ;

if ( (*ptr == '>' || *ptr == '<') && *(ptr-1) == ' ' )
   {                                   /* do file redirection */
   oldfile = mycli_outfp;              /* save old i/o handle */
   name = stpblk( ptr + 1);            /* skip any leading blanks */
   for ( pc = name; *pc && *pc != ' '; pc++ )
      ;                                /* find end of redirection name */
   if ( *pc )
      *pc++ = '\0';
   tempfh = Open( name, MODE_NEWFILE); /* open redirection */
   if ( tempfh == NULL)
      {
      strcpy( work, "Can't open redirection file!\n");
      WriteWork();
      return( FALSE );
      }
   if ( *ptr == '>' )
      mycli_outfp= tempfh;             /* redirect */
   else
      mycli_infp = tempfh;

   strcpy( ptr-1, pc );                /* delete out the redir. stuff */
   }                                   /* end of redirection open */

ptr = command;                         /* point to beginning of cmd string */

/* Look for endcli, def */
for (i=0; command_table[ i ].cmdname[ 0 ] != '\0'; i++)
   if (strncmp( ptr, &command_table[ i ].cmdname[ 0 ],
                strlen(&command_table[ i ].cmdname[ 0 ]) ) == 0)
      {
      found = TRUE;
      ptr = stpblk( &command[ strlen(command_table[i].cmdname) ] );
      (*command_table[ i ].cmdfunc)( ptr );     /* call the command */
      }

if ( !found)                           /* Not found, look for it on disk */
   executive( command );

if ( oldfile )                         /* close the redirection file(s) */
   {
   if ( mycli_outfp != oldfile )
      Close( mycli_outfp );
   if ( mycli_infp  != oldfile )
      Close( mycli_infp );             /* close up any redirection */
   mycli_outfp = mycli_infp = oldfile; /* restore the defaults */
   }

}

executive(s)
UBYTE  *s;
{
struct   FileLock *fl;
register UBYTE    *pc;
UBYTE    *wptr;
WORD     found;
WORD     batchON;

if ( *s == '\0' )
   return( FALSE );        /* no command so just return */

/* get the first token off of the command line into work. */
pc = &work[ 0 ];
while ( *s && (*pc = *s++) != ' ' )
   pc++;
*pc = '\0';                /* null terminate work[] */
s = stpblk( s );           /* skip spaces , s points at arguments */

strcpy( work2, work );     /* save first token in work2 */

strcpy(work, work2);       /* preserve the first token again */
   batchON = FALSE;
   found = FALSE;
   /* do the path search */
   pc = &path_string[ 0 ];
   while ( *pc )
      {
      wptr = &work [ 0 ];     /* set wptr to start of work[] */
      while ( *pc && (*wptr = *pc) != ';' )
         pc++, wptr++;        /* copy path prefix into work array */
      *wptr = '\0';           /* null terminate work[] */
      pc++;                   /* advance over the semi-colon */
      if ( work[ 0 ] == '.' ) /* dot means current dir */
         work[ 0 ] = '\0';
      strcat( work, work2 );  /* add the command */
      fl = Lock( work, ACCESS_READ);
      if ( fl != NULL)
         {
         UnLock( fl);
         found = TRUE;
         break;
         }
      }                       /* end of while */
   if ( !found )
      strcpy( work, work2);   /* restore the command in work[] */
   strcpy( work2, s);         /* put the params in work2[] */

strcat( work, " < CON:100/0/640/100/");       /* redirect input */

if ( *work2 )
   strcat( work, work2 );     /* add the parameters */

WriteWork();

if ( !Execute( work, 0L, mycli_outfp) )
   doserr();
}

doserr()
{
sprintf( work, "DOS error");
WriteWork();
}

/*
 * If a definition for the function key fkey exists ( 0-19), then
 * the translation for the function key is copied to the string
 * s, and this function returns 1.  Otherwise, no translation
 * exists, and this function returns 0;
 */
function_key( fkey, s )
int fkey;
UBYTE *s;
{
register WORD i;

if ( function_key_definitions[ fkey ] != 0)
   {
   for( i = 0; function_key_definitions[ fkey ][ i ] != 0; i++)
      s[ i ] = function_key_definitions[ fkey ][ i ];
   s[ i ] = '\0';
   return( TRUE );
   }
return( FALSE );
}

showprompt()
{
sprintf(work, "MYprompt >");
WriteWork();
}

getcommand(s)
UBYTE  *s;
{
register WORD col, i;
WORD  insert = TRUE;
UBYTE event_buffer[ 32 ];
UBYTE c;

for ( col = 0; col < 72; col++)
   s[ col ] = '\0';              /* initialize string to NULLs */
col = 0;

while ( TRUE )
   {
   Read(mycli_infp, &c, 1L);
   switch( c )
      {
      case 8:              /* Backspace key */
         if (col)
            {
            Write(mycli_outfp, "\x08\x9bP", 3L);
            col--;
            s[ col ] = '\0';
            strcat( &s[ 0 ], &s[ col+1 ]);
            }
         continue;
      case 0x09:                       /* CTRL-I or insert key */
         insert = !insert;
         continue;
      case 0x7f:                       /* DEL key */
         Write(mycli_outfp, "\x9bP", 2L);
         s[ col ] = '\0';
         strcat( &s[ 0 ], &s[ col+1 ]);
         continue;
      case 10: case 13:                /* CR or LF are end line chars */
         Write(mycli_outfp, "\n", 1L);
         if ( s[ 0 ] != '\0')
            {
            strcpy( &cmdbuf[ currcmd ][ 0 ], s );
            currcmd = ++currcmd & 15;  /* make counter wrap */
            }
         break;
      case 24:               /* ^X is kill line char */
         while (col)
            {
            Write( mycli_outfp, "\x08 \x08", 3L);
            col--;
            s[ col ] = '\0';
            }
         continue;
      case 0x9b:
         Read( mycli_infp, &c, 1L);
         if ( isdigit( c ) )
            {
            i = 0;
            event_buffer[ i++ ] = c;
            while ( TRUE )
               {
               Read( mycli_infp, &c, 1L);
               if ( c == '~')
                  break;
               event_buffer[ i++ ] = c;
               }
            event_buffer[ i ] = '\0';
            if ( function_key( atoi(event_buffer), s) )
               {
               sprintf( work, "%s\n", s);
               WriteWork();
               return( FALSE );
               }
            }
         else
            switch( c)
               {
               case ' ':      /* shifted cursor left and right */
                  Read( mycli_infp, &c, 1L);
                  if ( c == 'A') /* shift left */
                     while (col)
                        {
                        Write( mycli_outfp, "\x08", 1L);
                        col--;
                        }
                  else           /* shift right */
                     while ( col < 70 && s[ col ] != '\0' )
                        {
                        Write(mycli_outfp, "\x9bC", 2L);
                        col++;
                        }
                  break;
               case 'A': case 'B':
                  if ( c == 'A' )
                     currcmd = --currcmd & 15;
                  else
                     currcmd = ++currcmd & 15;
                  for ( i = 0; i < 72; i++)
                     s[ i ] = '\0';
                  strcpy( s, &cmdbuf[ currcmd ][ 0 ]);
                  col = strlen( s );
                  Write( mycli_outfp, "\r\x9bK", 3L);
                  showprompt();
                  strcpy( work, s);
                  WriteWork();
                  break;
               case 'C':                     /* cursor right */
                  if ( col < 70 && s[ col ] != '\0' )
                     {
                     Write(mycli_outfp, "\x9bC", 2L);
                     col++;
                     }
                  break;
               case 'D':                     /* cursor left */
                  if ( col)
                     {
                     Write(mycli_outfp, "\x08", 1L);
                     col--;
                     }
                  break;
               }
            continue;
         default:
            if ( c >= ' ' && c <= '~' )
               if (insert)
                  {
                  if ( strlen(s) < 70)
                     {
                     strcpy( work2, &s[ col ]);
                     s[ col++ ] = c;
                     s[ col ] = '\0';
                     strcat( s, work2);
                     Write( mycli_outfp, "\x9b@", 2L);
                     }
                  }
               else if ( col < 70)
                  s[ col++ ] = c;
            Write( mycli_outfp, &c, 1L);
            continue;
         }
      break;
      }
   }

define_function_key(s)
UBYTE  *s;
{
register UBYTE *ptr;
register WORD  i;

ptr = s;                                     /* set up register */
if ( *ptr == '\0')
   {
   for ( i = 0; i < 20; i++)
      if (function_key_definitions[ i ])
         {
         sprintf(work, "F%-2d = %s\n", i+1, function_key_definitions[ i ]);
         WriteWork();
         }
   return( FALSE );
   }

i = atoi( ptr + 1 );
if ( ( *ptr != 'f' && *ptr != 'F') || (i < 1 || i > 20) )
   {
   strcpy( work,"\x1b[33mInvalid function key specified\x1b[0m\x1b[K\n");
   WriteWork();
   return( FALSE );
   }

ptr++;
if ( function_key_definitions[ --i ] )
   {
   FreeMem( function_key_definitions[ i ], (long)strlen(function_key_definitions[ i ])+1 );
   function_key_definitions[ i ] = 0L;
   }
while ( isdigit( *ptr ) )
   ptr++;
ptr = stpblk( ptr );
if ( *ptr )
   {
   function_key_definitions[ i ] = AllocMem((long)strlen(ptr)+1, MEMF_PUBLIC|MEMF_CLEAR);
   if (function_key_definitions[ i ] == 0)
      {
      strcpy( work, "\x1b[33mDefine Error: not enough memory\x1b[0m\x1b[K\n");
      WriteWork();
      }
   else
      strcpy(function_key_definitions[ i ], ptr);
   }
}

endcli()
{
register WORD i;

      for ( i = 0; i < 20; i++)
         if (function_key_definitions[ i ])
            FreeMem(function_key_definitions[ i ], (long)strlen(function_key_definitions[ i ])+1);
      Close( mycli_outfp);
      exit(0);
}


#ifdef AZTEC
UBYTE *stpblk(str)
register UBYTE *str;
{
   while( *str && isspace(*str) )
     str++;
   return ( str );
}

isdigit(c)
register UBYTE c;
{
return( (c >= '0') && (c <= '9' ) );   /* depends on ASCII relationships */
}

isspace(c)
register UBYTE c;
{
   if ( c == '\t' || c == ' ' || c == '\n' )
      return TRUE;
   return FALSE;
}
#endif   AZTEC
