/*
 * MyCLI - a replacement for the standard AmigaDos CLI
 * Programmed by Mike Schwartz (C)1985 MS Software, All Rights Reserved!
 * Feel free to give this software away, in any form, for no charge only.
 * Feel free to donate to:
 * MS Software
 * 344 Fay Way
 * Mountain View, CA 94043
 * 
 * Modified 27. July 1986 by W. Burmeister
 *
 *
 *
 */

#include "ctype.h"
#include "exec/types.h"
#include "exec/exec.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"
#include "devices/serial.h"

/*
 * External functions
 */
extern   unsigned char        *AllocMem();
extern   struct   MsgPort     *CreatePort();
extern   struct   FileLock    *CreateDir();
extern   struct   FileLock    *CurrentDir();
extern   struct   FileLock    *Lock();
extern   struct   FileHandle  *Open();

/*
 * forward references
 */
int   cd(), copy(), date(), define_function_key(), delete(),
      dir(), delete(), endcli(), help(), md(), newcli(),
      rename(), time(), type(), whatis(), stpchr();

unsigned char *stpblk();

      
/*
 * Tables
 */
struct {
   unsigned char  *cmdname;
   int            (*cmdfunc)();
   } command_table[] = {
   (unsigned char *)"cd", &cd,
   (unsigned char *)"chdir", &cd,
   (unsigned char *)"copy", &copy,
   (unsigned char *)"date", &date,
   (unsigned char *)"define", &define_function_key,
   (unsigned char *)"def", &define_function_key,
   (unsigned char *)"delete", &delete,
   (unsigned char *)"del", &delete,
   (unsigned char *)"dir", &dir,
   (unsigned char *)"erase", &delete,
   (unsigned char *)"endcli", &endcli,
   (unsigned char *)"help", &help,
   (unsigned char *)"makedir", &md,
   (unsigned char *)"md", &md,
   (unsigned char *)"newcli", &newcli,
   (unsigned char *)"rename", &rename,
   (unsigned char *)"ren", &rename,
   (unsigned char *)"type", &type,
   (unsigned char *)"whatis", &whatis,
   (unsigned char *)"\0", &help,
   };

unsigned char  *help_messages[] = {
   "cd         = current directory",
   "chdir      = current directory",
   "copy       = copy from one file to another",
   "date       = show current date and time",
   "def        = define a function key",
   "define     = define a function key",
   "del        = delete file or subdirectory",
   "delete     = delete file or subdirectory",
   "dir        = directory",
   "erase      = delete file or subdirectory",
   "endcli     = exit to previous cli",
   "help       = print this list",
   "makedir    = make a new subdirectory",
   "md         = make a new directory",
   "newcli     = birth another mycli task",
   "ren        = rename a file or directory",
   "rename     = rename a file or directory",
   "type       = view a file",
   "whatis     = converts AmigaDos Error Codes to text",
   0};

unsigned char  *function_key_definitions[20] = {
   0,0,0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0,0,0
   };

struct   {
   int   code;
   unsigned char  *message;
   } errorcodes[] = {

      103,    "insufficient free store",
      104,    "task table full",
      120,    "argument line invalid or too long",
      121,    "file is not an object module",
      122,    "invalid resident library during load",
      203,    "object already exists",
      204,    "directory not found",
      205,    "object not found",
      206,    "invalid window",
      210,    "invalid stram component name",
      212,    "object not of required type",
      213,    "disk not validated",
      214,    "disk write-protected",
      215,    "rename across devices attempted",
      216,    "directory not empty",
      218,    "device not mounted",
      220,    "comment too big",
      221,    "disk full",
      222,    "file is protected from deletion",
      223,    "file is protected from writing",
      224,    "file is protected from reading",
      225,    "not a DOS disk",
      226,    "no disk in drive",
      209,    "packet request type unknown",
      211,    "invalid object lock",
      219,    "seek error",
      232,    "no more entries in directory",
      0, 0};

/*
 * Globals
 */
struct   FileLock       *worklock;
struct   InfoData       disk_info;
struct   FileInfoBlock  fib;
struct   FileHandle     *workfp;

unsigned char     work[512];
unsigned char     buf[512];
unsigned char     work2[512];
unsigned char     temp[80];

/*
 * MyCli globals
 */
struct   FileHandle     *mycli_infp;
struct   FileHandle     *mycli_outfp;

unsigned char     *prompt_string = 0;
unsigned char     current_directory[80];
int               mycli_id = 1;

main(argc, argv)
int   argc;
unsigned char  *argv[];
{
   if (argc == 2)
      mycli_id = atoi(argv[1]);
   sprintf(work, "Raw:0/%d/639/%d/MyCli[%d]", (mycli_id-1)*8, 199-(mycli_id-1)*16, mycli_id);
   mycli_infp = Open(work, MODE_NEWFILE);
   if (mycli_infp == 0) {
      printf("Can't open window\n");
      exit(1);
      }
   mycli_outfp = mycli_infp;
   sprintf(work, "%cMyCli\nModified By Willi Burmeister\n\n", 0x0c);
   WriteWork();

   current_directory[0] = '\0';
   cd("df0:");

   while (1) {
      showprompt();
      getcommand(buf);
      if (buf[0] != '\0') 
          CommandInterpreter(buf);
      }
   }

WriteWork() {
   Write(mycli_outfp, work, strlen(work));
   }


CommandInterpreter(command)
unsigned char  *command;
{
   int   i;

   command = stpblk(command);
   /*
    * 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.
    */
   for (i=0; command_table[i].cmdname[0] != '\0'; i++)
      if (
         strncmp(command,
              command_table[i].cmdname,
              strlen(command_table[i].cmdname))
               == 0) {
         (*command_table[i].cmdfunc)
            (stpblk(&command[strlen(command_table[i].cmdname)]));
         goto FinishedCommand;
         }
   /*
    * Not found, so look for it on the disk.
    */
   executive(stpblk(&command[0]));
FinishedCommand:  ;
   }

executive(s)
unsigned char  *s;
{
   struct   FileLock *fl;
   unsigned char     *pc;

   /*
    * get the first token off of the command line into work.
    */
   pc = work;
   while ((*pc = *s) != ' ' && *pc != '\0') {
      pc++;
      s++;
      }
   *pc = '\0';
   strcpy(work2, work);       /* save first token in work2 */


   /*
    * skip forward to 1st argument.  This is what is passed as arguments
    * to the executed cli batch file or amigados program to be invoked.
    */

   s = stpblk(s);

   /*
    * .cli files are batch files to be run by the
    * standard cli, using the execute command.
    */

   strcpy(work, work2);       /* preserve the first token again */
   strcat(work2, ".cli");
   fl = Lock(work2, ACCESS_READ);
   if (fl == 0)               /* cli batch file doesn't exist */
      strcpy(work2, work);    /* restore first token */
   UnLock(fl);                /* free the lock obtained */
   if (*s != '\0') {          /* parameters are to be passed */
      strcat(work2, " ");     /* add a space */
      strcat(work2, s);       /* and the parameters */
      }
   if (!Execute(work2, 0, mycli_outfp))
      doserr();
execfini:  ;
   }

doserr() {
   sprintf(buf, "%d", IoErr());
   whatis(buf);
   }


cd(s)
unsigned char  *s;
{
   int   i;

   strcpy(temp, current_directory);
   if (*s == '\0') {
      sprintf(work, "%-30s\n", current_directory);
      WriteWork();
      }
   else {
      if (*s == '/') {
         s++;
         for (i=strlen(current_directory);
              current_directory[i] != '/' && current_directory[i] != ':';
              i--);
         current_directory[i+1] = '\0';
         strcat(current_directory, s);
         }
      else if (stpchr(s, ':') == 0) {
         if (current_directory[strlen(current_directory)-1] != ':')
            strcat(current_directory, "/");
         strcat(current_directory, s);
         }
      else
         strcpy(current_directory, s);
      worklock = Lock(current_directory, ACCESS_READ);
      if (worklock == 0) {
         doserr();
         strcpy(current_directory, temp);
         }
      else if (Examine(worklock, &fib)) {
         if (fib.fib_DirEntryType > 0) {
            worklock = CurrentDir(worklock);
            if (worklock != 0)
               UnLock(worklock);
            }
         else {
            sprintf(work, "%c[36mCD Error: not a directory (%d) %c[0m\n",
                    0x1b, fib.fib_DirEntryType, 0x1b);
            WriteWork();
            strcpy(current_directory, temp);
            }
         }
      else
         doserr();
      }
   }

copy(s)
unsigned char  *s;
{
   unsigned    char  *pc;
   struct      FileHandle  *copyin;
   struct      FileHandle  *copyout;
   int         iosize;
   int         actual;
   unsigned    char        *copybuf;

   if (strncmp(s, "from", 4) == 0)
      s = stpblk(&s[4]);
   for (pc = s; !isspace(*pc); pc++);
   *pc++ = '\0';
   pc = stpblk(pc);
   if (strncmp(pc, "to", 2) == 0)
      pc = stpblk(&pc[2]);

/*  printf("copy from %s to %s\n", s, pc);  */
   /*
    * check from filename for console.
    */
   if (strncmp(s, "con:", 4) == '*')
      copyin = mycli_infp;
   else
      copyin = Open(s, MODE_OLDFILE);
   if (copyin == 0)
      doserr();
   /*
    * second parameter
    */
   else {
      /*
       * console device?
       */
      if (strncmp(pc, "con:", 4) == 0)
         copyout = mycli_outfp;
      /*
       * no second parameter?
       */
      else if (*pc == '\0') {
         /*
          * separate filename from path specification/filename
          * or device:path/filename or simple filename
          * in from field.
          */
         pc = &s[strlen(s)-1];   /* end of string */
         while (pc != s && *pc != ':' && *pc != '/')
            pc--;
         /*
          * build an appropriate file specification for output
          * file.
          */
         strcpy(work, current_directory);
         /*
          * from device:filespec?
          */
         if (*pc == ':') {
            pc++;
            strcat(work, pc);
            }
         else {
            strcat(work, "/");
            /*
             * don't want //
             */
            if (*pc == '/')
               pc++;
            strcat(work, pc);
            }
/*  printf("Openning %s for output\n", work);  */
         copyout = Open(work, MODE_NEWFILE);
         }
      else {
/*  printf("Openning %s for output\n", pc);  */
        copyout = Open(pc, MODE_NEWFILE);
         }
      if (copyout == 0) {
         doserr();
         Close(copyin);
         }
      else {
         /*
          * here is how to determine the length of a
          * file.  This is the most desirable Amount
          * to read from the from file, because it
          * requires one head seek, and allows the
          * entire track buffer to be used for the
          * read.
          */
         iosize = Seek(copyin, 0, OFFSET_END);
         iosize = Seek(copyin, 0, OFFSET_BEGINING);

         /*
          * maybe there isn't enough memory to hold
          * the entire from file.  the following
          * algorithm determines whether 1/2 the
          * file file, 1/4, 1/8, etc., will fit
          * in memory at a time.  if 512 bytes can't
          * be allocated, then there is not enough
          * memory to do the copy at all.
          */
         do {
            copybuf = AllocMem(iosize, MEMF_PUBLIC|MEMF_CLEAR);
            if (copybuf == 0)
               iosize = iosize/2;
            }
         while (copybuf == 0 & iosize > 512);
         if (copybuf == 0) {
            sprintf(work,
                    "%c[36mCopy Error: Not Enough Memory%c[0m\n",
                    0x1b, 0x1b);
            WriteWork();
            }
         else
            do {
               actual = Read(copyin, copybuf, iosize);
               if (Write(copyout, copybuf, actual) != actual) {
                  doserr();
                  break;
                  }
               }
            while (actual == iosize);
         if (copyin != mycli_infp)
            Close(copyin);
         if (copyout != mycli_outfp)
            Close(copyout);
         FreeMem(copybuf, iosize);
         }
      }
   }

date(s)
unsigned char  *s;
{
   struct   DateStamp   dss;

   DateStamp(&dss);
   dates(work, &dss);
   times(work2, &dss);
   strcat(work, work2);
   strcat(work, "\n");
   WriteWork();
   }

define_function_key(s)
unsigned char  *s;
{
   int   i;

   if (s[0] == '\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();
            }
         }
   else if (s[0] == 'f' || s[0] == 'F') {
      s++;
      i = atoi(s);
      if (i < 1 || i > 20) {
         sprintf(work, "%c[36mInvalid function key%c[0m\n", 0x1b, 0x1b);
         WriteWork();
         }
      else {
         i--;
         if (function_key_definitions[i])
            FreeMem(function_key_definitions[i], strlen(function_key_definitions[i])+1);
         while (isdigit(*s))
            s++;
         s = stpblk(s);
         if (*s != '\0') {
            function_key_definitions[i] = AllocMem(strlen(s)+1, MEMF_PUBLIC|MEMF_CLEAR);
            if (function_key_definitions[i] == 0) {
               sprintf(work, "%c[36mDefine Error: not enough memory%c[0m\n",
                        0x1b, 0x1b);
               WriteWork();
               }
            else
               strcpy(function_key_definitions[i], s);
            }
         }
      }
   else {
      sprintf(work,
              "%c[36mDefine Error: invalid function key specified%c[0m\n",
              0x1b, 0x1b);
      WriteWork();
      }
   }

delete(s)
unsigned char  *s;
{
   if (!DeleteFile(s))
      doserr();
   }

dir(s)
unsigned char  *s;
{
   int   filecount, bytecount, blockcount, dircount;

   filecount = bytecount = blockcount = dircount = 0;

   if (*s == '\0')
      strcpy(work, current_directory);
   else
      strcpy(work, s);
   worklock = Lock(work, ACCESS_READ);
   if (worklock == 0)
      doserr();
   else {
      if (!Examine(worklock, &fib))
         doserr();
      else {
         sprintf(buf, "\nDirectory of %s\n", work);
         Write(mycli_outfp, buf, strlen(buf));
         if (!Info(worklock, &disk_info))
            doserr();
         else {
            sprintf(work,
"%c[7m    Unit: %2d Errors: %3d  BlockSize: %3d Blocks: %4d  Blocks Used: %4d    %c[0m\n",
               0x1b,
               disk_info.id_UnitNumber,
               disk_info.id_NumSoftErrors,
               disk_info.id_BytesPerBlock,
               disk_info.id_NumBlocks,
               disk_info.id_NumBlocksUsed,
               0x1b);
            WriteWork();
            }
         sprintf(work,
          "%c[7m%-30s Prot  Size  Blocks Comment                   %c[0m\n",
          0x1b, "FileName", 0x1b);
         WriteWork();
         if (fib.fib_DirEntryType < 0) {
            showfib();
            if (fib.fib_DirEntryType > 0)
               dircount++;
            else
               filecount++;
            bytecount += fib.fib_Size;
            blockcount += fib.fib_NumBlocks;
            }
         else
            while(ExNext(worklock, &fib)) {
               showfib();
               if (fib.fib_DirEntryType > 0)
                  dircount++;
               else
                  filecount++;
               bytecount += fib.fib_Size;
               blockcount += fib.fib_NumBlocks;
               if (!pause())
                  break;
               }
         sprintf(work, "%c[7m%3d Subdirectories %3d Files %7d Bytes %6d Blocks %7d Bytes Free %c[0m\n",
            0x1b,
            dircount, filecount, bytecount, blockcount,
            (disk_info.id_NumBlocks - disk_info.id_NumBlocksUsed) *
                                             disk_info.id_BytesPerBlock,
            0x1b);
         WriteWork();
         UnLock(worklock);
         }
      }
   }

showfib() {
   sprintf(work, "%-30s ", fib.fib_FileName);
   WriteWork();
   if (fib.fib_DirEntryType > 0) {
      sprintf(work, "SubDirectory       ");
      WriteWork();
      }
   else {
      sprintf(work, "%c%c%c%c %6d %6d ",
            (fib.fib_Protection&FIBF_READ) ? ' ' : 'R',
            (fib.fib_Protection&FIBF_WRITE) ? ' ' : 'W',
            (fib.fib_Protection&FIBF_EXECUTE) ? ' ' : 'E',
            (fib.fib_Protection&FIBF_DELETE) ? ' ' : 'D',
            fib.fib_Size,
            fib.fib_NumBlocks);
      WriteWork();
      }
   sprintf(work, "%s\n", fib.fib_Comment);
   WriteWork();
   }

endcli(s)
unsigned char  *s;
{
      Close(mycli_outfp);
      exit(0);
   }

help(s)
unsigned char  *s;
{
   int   i;

   sprintf(work,
           "%c[7m          MyCli Help          %c[0m\n", 0x1b, 0x1b);
   WriteWork();
   for (i=0; help_messages[i]; i++) {
      sprintf(work, "%s\n", help_messages[i]);
      WriteWork();
      if (!pause())
         break;
      }
   sprintf(work, "%c[7m          End of Help         %c[0m\n", 0x1b, 0x1b);
   WriteWork();
   }

md(s)
unsigned char  *s;
{
   worklock = CreateDir(s);
   if (worklock == 0)
      doserr();
   else
      