/* CLI shell program 11/85 REJ */
/* This program has been put into the public domain by
   Lunge Software.  Feel free to modify this, but please
   do not remove the credits.
   If you have any suggestions for improvement, please
   either send me mail.

      Written By:  Randell E Jesup
                   Lunge Software
                   2340 15th St
                   Troy, NY     12180

   To use this program, create a 'command file' named
'commands' in the sys: directory.  This file should consist
of lines with the following format:

nnnnnn       yyyy;zzzzz;xxxxx;...

'nnnnnn' is a string that you type from the cli (like 'ls').
'yyyy',etc, are the commands to be executed when the program
    sees 'nnnnnn'.  Multiple commands are seperated by ';'.

   Example:
ls       list
dl       delete
cp       copy
dlram    cd ram:;delete #?;cd df0:

Whatever you typed after the command will concatenated to
the end of the final command.  You may also type multiple
commands directly from the keyboard (i.e. 'list x;type y'.)

There is one other thing cpp does.  If you give it a line in
the command file like this:

prd      type %1 to %2; delete %1

it will replace %n with the nth argument you entered from
the keyboard.  All arguments after the highest used by %n
will be appended to the end of the last command line.

example: 'prd test prt:' -> 'type test to prt:; delete test'

I have a number of plans for enhancements to this, so get
word to me soon with any ideas you might have.  This code
may not be the prettiest, but it works.
A problem in 1.0 caused me to have to simulate the 'cd'
command.  As soon as I get 1.1, I will update this (if it
was fixed.)

   There are certain things that don't work exactly as they
do under cli (yet).  Ctrl-C won't be passed through, and if
you use 'xxx ?' it won't work, and any program that wants
input from the current window won't work.  To solve this,
redirect input to '*' or a 'con' window.

Good luck, and have fun!
*/

#include "stdio.h"
#include "libraries/dos.h"

extern char *strrchr(),*stpblk(),*strchr();

char curr_dir[100];
char tmpbuf[256];

int old_lock,new_lock;

struct CMD {
   struct CMD *Next_CMD;
   char   *string;
   char   *replace;
} *CMD_List[16], *End[16], temp;

char buff[256];
char buff2[256];

FILE *cf;

int input_file,output_file;

extern char *malloc();

long *
alloc(lth)
register unsigned lth;
{
   register char *ptr;

   if(!(ptr = malloc(lth)))
   {  printf("Not enough memory for getting %d bytes", lth);
      exit(20);
   }
   return((long *) ptr);
}

/*
 * This is a pain!  I have to handle cd as a special case!
 */
void exec_cmd(p)
   char *p;
{
      char *q;

      if ((*p == 'c') && (*(p+1) == 'd') && ((*(p+2) == ' ')
          || (*(p+2) == '\0')))
      {  q = p + 1;
         while (*++q == ' ') ;     /* skip blanks */
         if (*q == '\0')
         {  printf(curr_dir);
            printf("\n");
         } else {
            while (*q == '/')
            {  register char *t;

               q++;
               t = strrchr(curr_dir,'/');
               if (t == NULL)
               {  t = strchr(curr_dir,':');
                  if (t++ == NULL)
                  {  printf("Bad Dir Name!");
                     break;
                  }
               }
               *t = '\0';
            }
            if ((*q != ' ') && (*q != '\0'))
            {  if (strchr(q,':') != NULL)
                  if (*q == ':')
                  {  *(strchr(curr_dir,':')) = '\0';
                     strcat(curr_dir,q);
                  } else strcpy(curr_dir,q);
               else {
                  if (curr_dir[strlen(curr_dir)-1] != ':')
                     strcat(curr_dir,"/");
                  strcat(curr_dir,q);
               }
            }
            new_lock = Lock(curr_dir,SHARED_LOCK);
            if (new_lock != NULL)
            {  old_lock = CurrentDir(new_lock);
               UnLock(old_lock);
            } else printf("Directory %s does not exist\n",q);
         }
      } else Execute(p,0,output_file);
}

void command_loop()
/* command arrays are initialized */
/* now loop, replacing commands, until "stop" is seen */
{
/* current dir is set to df0:, as there is no way to find
 * out what the current dir is.
 * If you pass a 'cd xxx' command to dos, it'll execute it,
 * but neither the program's or the cli's dir will be
 * changed.  This means it must be simulated.  Also, if you
 * type this from dos:
 * 'cd devs:', 'cd :', 'cd', it will say you are in the
 * 'devs:' directory, but in reality you are in df0:
 * (assuming the disk is in df0:).  This works correctly
 * here.
 */
   strcpy(curr_dir,"df0:");
   new_lock = Lock(curr_dir,SHARED_LOCK);
   old_lock = CurrentDir(new_lock);
   UnLock(old_lock);
   for(;;)
   {  int total;
      struct CMD *ptr;
      int i;
      register char *p,*q,*s,*t;

      printf("cpp> ");
      total = 0;
      fgets(tmpbuf,256,stdin);  /* get an input line */
      s = p = tmpbuf;
      while ((*p != '\n') && (*p != '\0'))
         p++;
      *p = '\0';                /* strip newline */
      if (tmpbuf[0] == '\0') continue;
      if (!strcmp(tmpbuf,"stop")) break;
      while (*s != NULL)
      {  q = strchr(s,';'); /* break multiple input cmds  */
         if (q == NULL)     /* copy 1 at a time into buff */
         {  strcpy(buff,s);
            *s = NULL;
         } else {
            t = buff;
            while (s < q)
               *t++ = *s++; /* copy first command into buff */
            s++;
            *t = '\0';
         }
         sscanf(buff,"%s",temp.string);
         for (i=0;i<strlen(temp.string);i++)
            total += temp.string[i];
         total &= 15;
         ptr = CMD_List[total];
/*
 * search through the list for the command
 */
         while (ptr != NULL)
         {  if (!strcmp(ptr->string,temp.string))
            {  char *u;
               struct { char *x;} args[10];
               int n;
               int m = -1;
/* got a match for replace */
               strcpy(buff2,ptr->replace);
               u = buff2;
/* break out args, then replace '%n's */
               break_buff(&buff[strlen(ptr->string)],args);
               while ((u = strchr(u,'%')) != NULL)
               {  n = *(u+1) - '0' - 1;
                  insstr(u,args[n].x,args[n+1].x);
                  if (n > m)
                     m = n;
               }
               strcat(buff2," ");
               strcat(buff2,args[m+1].x);
               break;
            }
            ptr = ptr->Next_CMD;
         }
/* now, execute the command */
         if (ptr == NULL)     /* was replacement done ?  */
            p = buff;
         else
            p = buff2;
/*       printf("total cmd: %s\n",p);     */
         for (;p != NULL;)
         {  q = strchr(p,';'); /* each command may expand */
            if (q != NULL)     /* to multiple commands    */
               *q++ = '\0';
/*          printf("cmd: %s\n",p);        */
            exec_cmd(p);
            p = q;
         }   
      }
   }
}

main(argc, argv)
   int argc;
   char *argv[];
{
   char *FileName;
   char NullFile[30];
   register int i;

   NullFile[0] = '\0';
   temp.Next_CMD = NULL;
   for (i=0;i<16;i++)
   {
      CMD_List[i] = NULL;
      End[i] = NULL;
   }
   if (argc < 2)
      FileName = "sys:commands\0";
   else {
      argv++;
      if ((*argv)[0] == '?')
      {
         printf("command_file/K: ");
         FileName = NullFile;
         scanf("%s",FileName);
      }
      else FileName = argv[0];
   }
   printf("Filename: ");
   printf(FileName);
   printf("\n");
   cf = fopen(FileName,"r");
   if (cf == NULL)
   {  printf("Command file not found!\n");
      return(10);
   }
/*
 * right now, abbreviations can be no more than 30 chars
 */
   temp.string = (char *) alloc(31);
   temp.replace = (char *) alloc(226);
   while (fgets(buff,255,cf) != NULL)
   {
      register int total = 0;
      register char *p = buff;
      register char *s = temp.replace;
      
      while ((*p != '\n') && (*p != '\0'))
         p++;
      *p = '\0';                  /* drop newline char   */
      sscanf(buff,"%s",temp.string);
      p = &buff[strlen(temp.string)+2];
      while (*p == ' ')           /* drop leading spaces */
         p++;
      strcpy(s,p);                /* copy p to s */
      for (i=0;i<strlen(temp.string);i++)
         total += temp.string[i]; /* calculate hash index */
      total &= 15;
/*
 * insert abbreviation into 1 of 16 linked lists.  This is
 * done to reduce search time, and may not be needed.
 */
      if (CMD_List[total] == NULL)
      {  CMD_List[total] = (struct CMD *)
                                  alloc(sizeof(struct CMD));
         End[total] = CMD_List[total];
         *CMD_List[total] = temp;
      } else {
         End[total]->Next_CMD = (struct CMD *)
                                  alloc(sizeof(struct CMD));
         End[total] = End[total]->Next_CMD;
         *End[total] = temp;
      }
      End[total]->string = (char *)
                                 alloc(strlen(temp.string));
      strcpy(End[total]->string,temp.string);
      End[total]->replace = (char *)
                                alloc(strlen(temp.replace));
      strcpy(End[total]->replace,temp.replace);
   }
/* get input and output files */
   input_file = Input();
   output_file = Output();
   printf("Command Pre-Processor v1.00\n");
   printf(" courtesy Lunge Software Development\n");
   buff[0] = '\0';
   buff2[0] = '\0';
   command_loop();
   printf("Command Pre-Processing terminated\n");
}

break_buff(src,args)   /* parse arguments */
   char *src;
   struct { char *x;} args[];
{
   int i = 0;

   src = stpblk(src);         /* skip leading blanks */
   args[0].x = src;
   while (*src != '\0')       /* delims = \0 and space */
   {  if (*src == ' ')
      {  args[++i].x = ++src;
         src = stpblk(src);
      } else
         ++src;
   }
   for (i++;i<10;i++)         /* all others are null */
      args[i].x = src;
/* for (i=0;i<10;i++)
      printf("arg[%d] = %s\n",i,args[i].x);  */
}

insstr(dest,src,end)
/*
 * insert a string at a given position.  string starts at
 * src and goes to end-1
 */
   char *dest,*src,*end;
{
   char tmp[256];

   strcpy(tmp,&dest[2]);
/*   printf("start=%d, end=%d, remainder=%s, replace=%s\n",
           src,end,tmp,src);           */
   while (src != end)
      *dest++ = *src++;
   strcpy(dest,tmp);
}

/*
   There are various debugs commented out in the code. If
   you wish to understand how this works in more detail,
   put them back in.

   Look for an Amiga version of Hack (similar to Rogue) in
   several months and an optimizing version of Forth, both
   from Lunge Software.  (And maybe a few other goodies as
   well!)

   Good luck to all of you out there.  */


/* ------------- eof -------------- */

