[net.micro.amiga] SHELL FOR AMIGA .. as promised

dillon@CORY.BERKELEY.EDU (Matt Dillon) (02/27/86)

	Has been posted to net.sources.  Since its small, and only one
part, and there are some people on the Arpa-net who do not know how to
ftp, I'm posting it here as well:

	The program is modularized to some extent, especially the file
expansion routines, to allow easy extraction (there has been a lot of
discussion about file expansion on net.micro.amiga)


#-----cut here-----cut here-----cut here-----cut here-----
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	command.c
#	execom.c
#	globals.c
#	main.c
#	manual
#	set.c
#	shell.h
#	sub.c
# This archive created: Thu Feb 27 10:39:19 1986
export PATH; PATH=/bin:$PATH
if test -f 'command.c'
then
	echo shar: will not over-write existing file "'command.c'"
else
cat << \!Funky!Stuff! > 'command.c'

/*
 * COMMAND.C
 *
 * Matthew Dillon, 24 Feb 1986
 *
 * Most of the commands are in here (the variable commands are in set.c)
 *
 */

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

extern struct FileLock  *CreateDir(), *CurrentDir(), *ParentDir();
extern struct FileLock  *Lock(), *DupLock();
extern struct FileLock  *cdgo();
extern char *AllocMem();

extern long disp_entry();

struct FileLock *Clock;

do_run(str)
char *str;
{
   register int i, len;
   register char *build, *ptr;
   char from[128];
   char to[128];

   from[127] = to[127] = '\0';
   strcpy (from, "<*");
   strcpy (to, ">*");
   for (i = 1, len = 0; i < ac; ++i) {
      ptr = (*av[i] == '>') ? to : ((*av[i] == '<') ? from : NULL);
      if (ptr) {
         strncpy (ptr, av[i], 126);
         if (!ptr[1]) {
            av[i] = "";
            if (av[++i] == NULL)
               goto rerr;
            strncat (ptr, av[i], 125);
         }
         av[i] = "";
         break;
      }
      len += strlen(av[i]) + 1;
   }
   len += strlen(av[0]) + strlen(from) + strlen(to) + 4;
   build = malloc(len);
   strcpy (build, av[0]);
   strcat (build, " ");
   strcat (build, from);
   strcat (build, " ");
   strcat (build, to);
   for (i = 1; i < ac; ++i) {
      if (*av[i]) {
         strcat (build, " ");
         strcat (build, av[i]);
      }
   }
   i = Execute (build, 0, 0);
   free (build);
   return ((i) ? 1 : -1);
rerr:
   puts ("redirection syntax error");
   return (-1);
}

do_number()
{
   return (1);
}

do_cat()
{
   FILE *fi;
   int i;
   char buf[256];

   for (i = 1; i < ac; ++i) {
      if (fi = fopen (av[i], "r")) {
         while (fgets (buf, 256, fi)) {
            fputs (buf, stdout);
            fflush (stdout);
         }
         fclose (fi);
      } else {
         perror (av[i]);
      }
   }
}

do_dir(garbage, com)
char *garbage;
{
   struct DPTR          *dp;
   struct InfoData      *info;
   char *name;
   int i, stat;
   long total = 0;

   if (ac == 1) {
      ++ac;
      av[1] = "";
   }
   for (i = 1; i < ac; ++i) {
      if ((dp = dopen (av[i], &stat)) == NULL)
         continue;
      if (com < 0) {
         info = (struct InfoData *)AllocMem(sizeof(struct InfoData), MEMF_PUBLIC);
         if (Info (dp->lock, info)) {
            int bpb = info->id_BytesPerBlock;
            printf ("Unit:%2d  Errs:%3d  Bytes: %-7d Free: %-7d\n",
                  info->id_UnitNumber,
                  info->id_NumSoftErrors,
                  bpb * info->id_NumBlocks,
                  bpb * (info->id_NumBlocks - info->id_NumBlocksUsed));
         } else {
            perror (av[i]);
         }
         FreeMem (info, sizeof(*info));
      } else {
         if (stat) {
            while (dnext (dp, &name, &stat))
               total += disp_entry (dp->fib);
         } else {
            total += disp_entry(dp->fib);
         }

      }
      dclose (dp);
   }
   printf ("TOTAL: %ld\n", total);
   return (1);
}


long
disp_entry(fib)
register struct FileInfoBlock *fib;
{
   char str[5];
   register char *dirstr;

   str[4] = '\0';
   str[0] = (fib->fib_Protection & FIBF_READ) ? '-' : 'r';
   str[1] = (fib->fib_Protection & FIBF_WRITE) ? '-' : 'w';
   str[2] = (fib->fib_Protection & FIBF_EXECUTE) ? '-' : 'x';
   str[3] = (fib->fib_Protection & FIBF_DELETE) ? '-' : 'd';
   dirstr = (fib->fib_DirEntryType < 0) ? "   " : "DIR";

   printf ("%s %6ld %s  %s\n", str, (long)fib->fib_Size, dirstr, fib->fib_FileName);
   return ((long)fib->fib_Size);
}


do_quit()
{
   exit (1);
}

do_echo(str)
char *str;
{
   if (strcmp (av[1], "-n") == 0) {
      fputs (next_word(next_word(str)), stdout);
   } else {
      puts (next_word(str));
   }
   fflush (stdout);
   return (1);
}


do_source(str)
char *str;
{
   register FILE *fi;
   char buf[256];

   fi = fopen (next_word(str), "r");
   if (fi == NULL) {
      printf ("Cannot open %s\n", str);
      return(-1);
   }
   ++H_stack;
   while (fgets (buf, 256, fi) != NULL) {
      buf[strlen(buf) - 1] = '\0';
      exec_command (buf);
   }
   --H_stack;
   fclose (fi);
   return (1);
}

/*
 * cd.  Additionally, allow '..' to get you back one directory in the path.
 */

do_cd()
{
   static char root[32];
   register struct FileLock *lock;
   register char *str, *ptr;
   register int notdone;

   if (ac != 2) {
      puts (root);
      Clock = cdgo (root, NULL);
      return (1);
   }
   str = av[1];
   while (*str == '/') {
      ++str;
      if (Clock)
         Clock = cdgo (NULL, Clock);
   }
   notdone = 1;
   while (notdone) {
      ptr = str;
      while (*str && *str != '/' && *str != ':')
         ++str;
      notdone = *str;
      *str++ = '\0';
      if (ptr == str - 1)
         continue;
      if (notdone == ':') {
         *(str-1) = notdone;
         notdone = *str;
         *str = '\0';
         strcpy (root, ptr);
         Clock = cdgo (root, NULL);
         *str = notdone;
      } else
      if (strcmp (ptr, "..") == 0) {
         if (Clock)
            Clock = cdgo (NULL, Clock);
      } else {
         if ((lock = cdgo (ptr, NULL)) == NULL)
            puts ("not found");
         else
            Clock = lock;
      }
   }
   return (1);
}


struct FileLock *
cdgo(ptr, lock)
char *ptr;
struct FileLock *lock;
{
   struct FileLock *newlock, *oldlock;

   if (lock)
      newlock = ParentDir (lock);
   else
      newlock = Lock (ptr, ACCESS_READ);
   if (newlock) {
      if (oldlock = CurrentDir (newlock))
         UnLock (oldlock);
   }
   return (newlock);
}


do_mkdir()
{
   register int i;
   register struct FileLock *lock;

   for (i = 1; i < ac; ++i) {
      if (lock = CreateDir (av[i])) {
         UnLock (lock);
         continue;
      }
      perror (av[i]);
   }
   return (1);
}

do_mv()
{
   if (ac != 3) {
      puts ("Rename required 2 arguments");
      return (-1);
   }
   if (Rename (av[1], av[2]))
      return (1);
   perror (NULL);
   return (-1);
}

do_rm()
{
   register int i;

   for (i = 1; i < ac; ++i) {
      if (!DeleteFile(av[i]))
         perror (av[i]);
   }
   return (1);
}


do_history()
{
   register struct HIST *hist;
   register int i = H_tail_base;

   for (hist = H_tail; hist; hist = hist->prev) {
      printf ("%3d %s\n", i, hist->line);
      ++i;
   }
   return (1);
}

do_mem()
{
   long cfree, ffree;
   extern long AvailMem();

   Forbid();
   cfree = AvailMem (MEMF_CHIP);
   ffree = AvailMem (MEMF_FAST);
   Permit();

   if (ffree)
      printf ("FAST memory:%10ld\n", ffree);
   printf ("CHIP memory:%10ld\n", cfree);
   printf ("Total -----:%5ld K\n", (ffree + cfree)/1024L);
}

/*
 * foreach var_name  ( str str str str... str ) commands
 * spacing is important (unfortunetly)
 *
 * ac=0    1 2 3 4 5 6 7
 * foreach i ( a b c ) echo $i
 * foreach i ( *.c )   "echo -n "file ->";echo $i"
 */

do_foreach()
{
   register int i, cstart, cend, old;
   register char *cstr, *vname, *ptr;

   if (ac < 2) {
      puts ("form: foreach i ( str str str.. ) command");
      return (-1);
   }
   cstart = i = (*av[2] == '(') ? 3 : 2;
   while (i < ac) {
      if (*av[i] == ')')
         break;
      ++i;
   }
   if (i == ac) {
      puts ("')' expected");
      return (-1);
   }
   ++H_stack;
   cend = i;
   vname = strcpy(malloc(strlen(av[1])+1), av[1]);
   cstr = compile_av (av, cend + 1, ac);
   for (i = cstart; i < cend; ++i) {
again:
      ptr = av[i];
      while (*ptr && *ptr != ' ')
         ++ptr;
      old = *ptr;
      *ptr = '\0';
      set_var (LEVEL_SET, vname, av[i]);
      exec_command (cstr);
      if (old) {
         while (*++ptr && (*ptr == ' ' || *ptr == 9));
         av[i] = ptr;
         if (*ptr)
            goto again;
      }
   }
   --H_stack;
   free (cstr);
   unset_var (LEVEL_SET, vname);
   free (vname);
   return (1);
}



!Funky!Stuff!
fi # end of overwriting check
if test -f 'execom.c'
then
	echo shar: will not over-write existing file "'execom.c'"
else
cat << \!Funky!Stuff! > 'execom.c'

/*
 * EXECOM.C
 *
 * Matthew Dillon, 24 Feb 1986
 *
 *    This code is particular hacked up and needs re-writing.
 *
 */

#include "shell.h"


struct COMMAND {
   int (*func)();
   int stat;
   int val;
   char *name;
};

struct MLIST {
   struct MLIST *next;
};

#define F_EXACT   0
#define F_ABBR    1

extern char *breakout();

extern int do_run(), do_number();
extern int do_quit(), do_set_var(), do_unset_var();
extern int do_echo(), do_source(), do_mv();
extern int do_cd(), do_rm(), do_mkdir(), do_history();
extern int do_mem(), do_cat(), do_dir();
extern int do_foreach();

static struct MLIST *Mlist;

static struct COMMAND Command[] = {
   do_run      ,  0,          0 ,   "\001",
   do_number   ,  0,          0 ,   "\001",
   do_quit     ,  0,          0 ,   "quit",
   do_set_var  ,  0, LEVEL_SET  ,   "set",
   do_unset_var,  0, LEVEL_SET  ,   "unset",
   do_set_var  ,  0, LEVEL_ALIAS,   "alias",
   do_unset_var,  0, LEVEL_ALIAS,   "unalias",
   do_echo     ,  0,          0 ,   "echo",
   do_source   ,  0,          0 ,   "source",
   do_mv       ,  0,          0 ,   "mv",
   do_cd       ,  0,          0 ,   "cd",
   do_rm       ,  0,          0 ,   "rm",
   do_mkdir    ,  0,          0 ,   "mkdir",
   do_history  ,  0,          0 ,   "history",
   do_mem      ,  0,          0 ,   "mem",
   do_cat      ,  0,          0 ,   "cat",
   do_dir      ,  0,          0 ,   "dir",
   do_dir      ,  0,         -1 ,   "devinfo",
   do_foreach  ,  0,          0 ,   "foreach",
   NULL        ,  0,          0 ,   NULL };

static char *
mpush(amount)
{
   struct MLIST *ml;

   ml = (struct MLIST *)malloc (amount + sizeof(*Mlist));
   ml->next = Mlist;
   Mlist = ml;
   return ((char *)Mlist + sizeof(*Mlist));
}

static char *
mpop()
{
   char *old = NULL;

   if (Mlist == NULL) {
      puts ("Internal MLIST error");
      fflush (stdout);
   } else {
      old = (char *)Mlist + sizeof(*Mlist);
      Free (Mlist);
      Mlist = Mlist->next;
   }
   return (old);
}

mrm()
{
   while (Mlist) {
      Free (Mlist);
      Mlist = Mlist->next;
   }
}

exec_command(base)
char *base;
{
   char *str;
   int i;
   if (!H_stack)
      add_history(base);
   strcpy (str = mpush(strlen(base) + 1), base);
   i = e_command(str);
   if (mpop() != str) {
      puts ("POP ERROR");
      fflush (stdout);
   }
   return (1);
}

static
e_command(base)
char *base;
{
   char *com, *start, *avline, *alias;
   char *s1, *s2;
   int flag = 0;
   int i, pcount, len, ccno;
loop:
   com = breakout (&base, &flag);
   if (*com == '\0') {
      if (flag & FL_EOL)
         return (1);
      goto loop;
   }
   alias = NULL;
   if ((ccno = find_command(com, F_EXACT)) < 0) {
      switch (flag & FL_MASK) {
      case FL_BANG:
         alias = get_history (com);
         replace_head (alias, base, flag);
         break;
      case FL_DOLLAR:
         alias = get_var (LEVEL_SET, com + 1);
         break;
      default:
         alias = get_var (LEVEL_ALIAS, com);
         break;
      }
      if (alias == NULL) {
         if ((ccno = find_command (com, F_ABBR)) < 0) {
            printf ("%s Command Not Found\n", com);
            return (-1);
         } else {
            goto good_command;
         }
      }

      if (*alias == '%')
         goto good_command;

      start = (!(flag & FL_EOC)) ? base : "";
      while (!(flag & FL_EOC)) {
         flag = FL_OVERIDE;
         breakout (&base, &flag);
      }

      com = mpush (strlen(alias) + strlen(start) + 2);
      strcpy (com, alias);
      strcat (com, " ");
      strcat (com, start);
      i = e_command (com);
      if (mpop() != com)
         puts ("ME BAE ERROR");
      if (i < 0)
         return (-1);
      if (flag & FL_EOL)
         return (1);
      goto loop;
   }
good_command:
   pcount = 0;
   i = pcount = 0;
   av[i] = mpush (strlen(com) + 1);
   ++pcount;
   strcpy (av[i++], com);
   while (!(flag & FL_EOC)) {
      com = breakout (&base, &flag);
      if (*com == '\0')
         continue;
      switch (flag & FL_MASK) {
      case FL_DOLLAR:
         av[i] = get_var (LEVEL_SET, com + 1);
         if (av[i] == NULL)
            av[i] = com;
         av[i] = strcpy(mpush (strlen(av[i]) + 1), av[i]);
         ++pcount;
         break;
      case FL_QUOTE:
      default:
         av[i] = com;
         break;
      }
      if (flag & FL_IDOLLAR) {
         for (s1 = av[i]; *s1 && *s1 != '$'; ++s1);
         if (*s1) {
            *s1 = '\0';
            s1 = get_var (LEVEL_SET, s1 + 1);
            if (s1) {
               register char *scr_str = mpush(strlen(av[i])+strlen(s1)+1);
               ++pcount;
               strcpy (scr_str, av[i]);
               strcat (scr_str, s1);
               av[i] = scr_str;
            }
         }
      }
      if (flag & FL_WILD) {   /*  av[i] has a wild card, expand it */
         int eac;
         char **eav, **ebase;

         eav = ebase = expand (av[i], &eac);   /* returns malloc'd av list */
         if (eav == NULL) {
            puts ("Null expansion");
            goto fail;
         }
         if (i + eac > MAXAV - 2) {
            free_expand (ebase);
            goto avovr;
         }
         for (; eac; --eac, ++eav) {
            av[i++] = strcpy(mpush(strlen(*eav)+1), *eav);
            ++pcount;
         }
         --i;
         free_expand (ebase);
      }
      if (++i > MAXAV - 2) {
avovr:
         puts ("AV overflow");
         goto fail;
      }
   }
   av[i] = NULL;
   ac = i;
   for (len = 0, i = 0; i < ac; ++i)
      len += strlen(av[i]) + 1;
   avline = mpush (len + 1);
   *avline = '\0';
   for (i = 0; i < ac; ++i) {
      strcat (avline, av[i]);
      if (i + 1 < ac)
         strcat (avline, " ");
   }
   if (*alias) {
      for (s2 = alias; *s2 && *s2 != ' '; ++s2);
      if (*s2) {
         *s2 = '\0';
         set_var (LEVEL_SET, alias + 1, next_word(avline));
         *s2 = ' ';
         s1 = strcpy(mpush(strlen(s2)+1), s2);
         i = e_command (s1);
         if (mpop() != s1)
            puts ("AL-LINE ERROR");
      }
   } else {
      i = (*Command[ccno].func)(avline, Command[ccno].val);
   }
   if (mpop() != avline) {
      puts ("AVLINE ERROR");
fail:
      i = -1;
   }
   while (pcount--)
      mpop();
   if (i < 0)
      return (i);
   if (flag & FL_EOL)
      return (1);
   goto loop;
}

static char *
breakout(base, flag)
register int *flag;
char **base;
{
   register char *str, *scr;
   register int oflag = *flag;

loop:
   *flag = 0;
   str = *base;
   while (*str == ' ' || *str == 9)
      ++str;
   switch (*str) {
   case '\0':
      *flag = FL_EOC | FL_EOL;
      *base = str;
      return (str);
   case '*':
   case '?':
      *flag = FL_WILD;
      break;
   case ';':
      *flag = FL_EOC;
      *str = '\0';
      *base = str + 1;
      return (str);
   case '\"':
      *flag = FL_QUOTE;
      break;
   case '$':
      *flag = FL_DOLLAR;
      break;
   case '%':
      *flag = FL_PERCENT;
      break;
   case '!':
      *flag = FL_BANG;
      break;
   default:
      break;
   }
   scr = str;
   for (;;) {
      switch (*scr) {
      case '$':
         if (scr != str)
            *flag |= FL_IDOLLAR;
         ++scr;
         break;
      case '*':
      case '?':
         *flag |= FL_WILD;
         ++scr;
         break;
      case ' ':
      case 9:
         if (!(oflag & FL_OVERIDE))
            *scr = '\0';
         *base = scr + 1;
         return (str);
      case '\"':                    /* Quote */
         del_char(scr);
         while (*scr && *scr != '\"') {
            if (*scr == '\\') {
               del_char(scr);
               if (*scr)
                  ++scr;
            } else {
               ++scr;
            }
         }
         if (*scr == '\"')
            del_char(scr);
         break;
      case '\0':
         *flag |= FL_EOL | FL_EOC;
         *base = scr;
         return (str);
      case ';':
         *flag |= FL_EOC;
         *base = scr + 1;
         *scr = '\0';
         return (str);
      case '^':
         ++scr;
         if (*scr) {
            *(scr - 1) = *scr & 0x1f;
            del_char (scr);
         }
         break;
      case '\\':
         del_char(scr);
         if (*scr)
            ++scr;
         break;
      default:
         ++scr;
      }
   }
}

del_char(str)
register char *str;
{
   for (; *str; ++str)
      str[0] = str[1];
}

static
find_command(str, arg)
char *str;
{
   int i;
   int len = strlen(str);

   if (*str >= '0'  &&  *str <= '9')
      return (1);
   for (i = 0; Command[i].func; ++i) {
      if (strncmp (str, Command[i].name, len) == 0) {
         if (arg == F_ABBR)
            return (i);
         if (strcmp (str, Command[i].name) == 0)
            return (i);
         return (-1);
      }
   }
   if (arg == F_ABBR)
      return (0);
   return (-1);
}



!Funky!Stuff!
fi # end of overwriting check
if test -f 'globals.c'
then
	echo shar: will not over-write existing file "'globals.c'"
else
cat << \!Funky!Stuff! > 'globals.c'

/*
 * GLOBALS.C
 *
 * Matthew Dillon, 24 Feb 1986
 *
 */


#include "shell.h"

struct HIST *H_head, *H_tail;

struct PERROR Perror[] = {
   103,  "insufficient free storage",
   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 stream component name",
   212,  "object not of required type",
   213,  "disk not validated",
   214,  "disk write protected",
   215,  "rename across devices",
   216,  "directory not empty",
   218,  "device not mounted",
   220,  "comment too long",
   221,  "disk full",
   222,  "file delete protected",
   223,  "file write protected",
   224,  "file read protected",
   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,  NULL
};

char *av[MAXAV];
int   H_len, H_tail_base, H_stack;
int   ac;
int   Debug;

int   S_histlen = 20;



!Funky!Stuff!
fi # end of overwriting check
if test -f 'main.c'
then
	echo shar: will not over-write existing file "'main.c'"
else
cat << \!Funky!Stuff! > 'main.c'

/*
 * MAIN.C
 *
 * Matthew Dillon, 24 Feb 1986
 *
 * el main routine.
 */

#include "shell.h"

char Inline[256];

main(argc, argv)
char *argv[];
{
   char *prompt;

   init_vars();
   init();
   strcpy (Inline, "source ");
   strcat (Inline, (argc == 2) ? argv[1] : "sys:.login");
   do_source (Inline, 0);
   for (;;) {
      if ((prompt = get_var (LEVEL_SET, V_PROMPT)) == NULL)
         prompt = "echo -n \"% \"";
      ++H_stack;
      exec_command (prompt);
      --H_stack;
      fflush (stdout);
      if (gets (Inline) == NULL)
         exit (0);
      exec_command (Inline);
   }
}

init()
{
}

init_vars()
{
   set_var (LEVEL_SET, V_PROMPT, "echo -n \"% \"");
   set_var (LEVEL_SET, V_HIST,   "20");
}


!Funky!Stuff!
fi # end of overwriting check
if test -f 'manual'
then
	echo shar: will not over-write existing file "'manual'"
else
cat << \!Funky!Stuff! > 'manual'

YASO (yet another shell [something])  Manual, By Matthew Dillon

Terms:	You may not charge money or any form of service for use of
	this program.  This program is public domain, and may be
	distributed freely as long as The author's name (and your
	name if you've hacked it majorly) and this message remains.

Startup:	sys:.login will be sourced by this program.  Usually, you
		put general aliases there.


The basic form of a command is:

	Command arg1 arg2.... argN

	* if the command is exactly a built in command, that built in
		command is run.
	* if the command is an alias, the alias is expanded 
	* if the command is a partial of a built in command, that built in
		command is run.
	* otherwise, the command is Execute()'d

The line scanner will do various substitutions before passing the line to
the command:

	Any set variables of the form $name is expanded to it's contents
		% set abc hello
		% echo $abc
		hello

	Any file-path with wildcards (example: *.c), is expanded (a.c b.c ..)
		% echo *.c
		execom.c a.c x.c

	Within quotes, ^c is expanded to it's control character
		% echo "^l"
		< the screen is cleared >

	You may do history substitution of the form:
		% !!				-previous command
		% !partial			-search history for command
		% !num				-a specific history #
	
	In most cases, you may use \ to overide a special character (there are
	some bugs with this one)
		% echo \*
		*

INTERNAL COMMANDS

	quit					-quit, done, vamoos
	set	name arg1 arg23....		-set a variable ($substitution)
	unset	name1 name2 ...			-unset variables
	alias	name "command;command..."	-whatever you want to alias
	unalias name1 name2 ...			-unalias aliases
	echo	[-n] arg1 arg2...		-echo args to screen
	source	file				-use a command file as input
	mv	file1 file2			-rename a file (rename only)
	cd	directory			-cd to a directory
	rm	file1 file2...			-rm files
	mkdir	dirname dirname...		-make directories
	history					-show history list
	mem					-show memory statistics
	cat	file1 file2...			-simple file dump to screen
	dir	dir/file dir/file..		-get directory
	devinfo	[device]			-get device info 
	foreach var ( arg arg arg ) command	-(see below)

VARIABLES:

	Two variables are reserved currently:

	_history				-# lines to remember
	_prompt					-command which causes your 
						 prompt.


SPECIFICS/SPECIAL COMMANDS:

echo [-n] arg ...
	The -n option causes a newline NOT to be printed

foreach example: (assume the files a.c b.c c.c and d.c are in the curr. dir)
	% foreach i ( *.c ) "echo -n hello;echo $i"
	hello a.c
	hello b.c
	hello c.c
	hello d.c
	%

REDIRECTION:

	You may redirect only external commands (sorry).  Currently, the
file name MUST BE NEXT TO THE '>' or '<'  '>charlie'.  However, the
redirection args can be anywhere on the argument line.  The default
redirection is to '*'.



BUGS:
	An OS bug in Execute()... 30+ bytes are lost every time an external
command is executed.
!Funky!Stuff!
fi # end of overwriting check
if test -f 'set.c'
then
	echo shar: will not over-write existing file "'set.c'"
else
cat << \!Funky!Stuff! > 'set.c'

/*
 * SET.C
 *
 * Matthew Dillon, 24 Feb 1986
 *
 */

#include "shell.h"
#define MAXLEVELS 3

struct MASTER {
   struct MASTER *next;
   struct MASTER *last;
   char *name;
   char *text;
};

static struct MASTER *Mbase[MAXLEVELS];

char *
set_var(level, name, str)
register char *name, *str;
{
   register struct MASTER *base = Mbase[level];
   register struct MASTER *last;

   while (base != NULL) {
      if (strcmp (name, base->name) == 0) {
         Free (base->text);
         goto gotit;
      }
      last = base;
      base = base->next;
   }
   if (base == Mbase[level]) {
      base = Mbase[level] = (struct MASTER *)malloc (sizeof(struct MASTER));
      base->last = NULL;
   } else {
      base = (struct MASTER *)malloc (sizeof(struct MASTER));
      base->last = last;
      last->next = base;
   }
   base->name = malloc (strlen(name) + 1);
   strcpy (base->name, name);
   base->next = NULL;
gotit:
   base->text = malloc (strlen(str) + 1);
   strcpy (base->text, str);
   return (base->text);
}

char *
get_var (level, name)
register char *name;
{
   register struct MASTER *base = Mbase[level];

   while (base != NULL) {
      if (strcmp (name, base->name) == 0)
         return (base->text);
      base = base->next;
   }
   return (NULL);
}


unset_var (level, name)
char *name;
{
   register struct MASTER *base = Mbase[level];
   register struct MASTER *last = NULL;

   while (base != NULL) {
      if (strcmp (name, base->name) == 0) {
         if (base != Mbase[level])
            last->next = base->next;
         else
            Mbase[level] = base->next;
         if (base->next != NULL)
            base->next->last = last;
         if (base == Mbase[level])
            Mbase[level] = base->next;
         Free (base->name);
         Free (base->text);
         Free (base);
         return (1);
      }
      last = base;
      base = base->next;
   }
   return (-1);
}


do_unset_var(str, level)
char *str;
{
   int i;

   for (i = 1; i < ac; ++i)
      unset_var (level, av[i]);
   return (1);
}

do_set_var(command, level)
char *command;
{
   register struct MASTER *base = Mbase[level];
   register char *str;

   if (ac == 1) {
      while (base) {
         printf ("%-10s %s\n", base->name, base->text);
         base = base->next;
      }
      return (1);
   }
   if (ac == 2) {
      str = get_var (level, av[1]);
      if (str)
         printf ("%-10s %s\n", av[1], str);
      else
         set_var (level, av[1], "");
   }
   if (ac > 2)
      set_var (level, av[1], next_word (next_word (command)));
   if (*av[1] == '_') {
      S_histlen = (str = get_var(LEVEL_SET, V_HIST)) ? atoi(str) : 0;

      if (S_histlen < 2)   S_histlen = 2;
   }
   return (1);
}


!Funky!Stuff!
fi # end of overwriting check
if test -f 'shell.h'
then
	echo shar: will not over-write existing file "'shell.h'"
else
cat << \!Funky!Stuff! > 'shell.h'

/*
 * SHELL.H
 *
 * Matthew Dillon, 24 Feb 1986
 *
 */

#define MAXAV        128            /* Max. # arguments */

#define LEVEL_SET    0
#define LEVEL_ALIAS  1

#define M_RESET      0
#define M_CONT       1

#define V_PROMPT     "_prompt"      /* Special variable names */
#define V_HIST       "_history"

#define FL_DOLLAR    0x01  /* One of the following */
#define FL_BANG      0x02
#define FL_PERCENT   0x04
#define FL_QUOTE     0x08
#define FL_IDOLLAR   0x10  /* Any or all of the following may be set */
#define FL_EOC       0x20
#define FL_EOL       0x40
#define FL_OVERIDE   0x80
#define FL_WILD      0x100
#define FL_MASK      (FL_DOLLAR|FL_BANG|FL_PERCENT|FL_QUOTE)

#include <lattice/stdio.h>

struct HIST {
   struct HIST *next, *prev;     /* doubly linked list */
   char *line;                   /* line in history    */
};

struct PERROR {
   int errnum;                   /* Format of global error lookup */
   char *errstr;
};

struct DPTR {                    /* Format of directory fetch pointer */
   struct FileLock *lock;        /* lock on directory   */
   struct FileInfoBlock *fib;    /* mod'd fib for entry */
};

extern struct HIST *H_head, *H_tail;
extern struct PERROR Perror[];
extern struct DPTR *dopen();
extern char *set_var(), *get_var(), *next_word();
extern char *get_history(), *compile_av();
extern char *malloc(), *realloc(), *strcpy(), *strcat(), *AllocMem();
extern char **expand();

extern char *av[];
extern char *Current;
extern int  H_len, H_tail_base, H_stack;
extern int  ac;
extern int  Debug;
extern int  S_histlen;



!Funky!Stuff!
fi # end of overwriting check
if test -f 'sub.c'
then
	echo shar: will not over-write existing file "'sub.c'"
else
cat << \!Funky!Stuff! > 'sub.c'

/*
 * SUB.C
 *
 * Matthew Dillon, 24 Feb 1986
 *
 */


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

#define HM_STR 0
#define HM_REL 1
#define HM_ABS 2

extern struct FileLock *Lock(), *DupLock(), *CurrentDir();
extern struct FileLock *Clock;

char *
next_word(str)
register char *str;
{
   while (*str  &&  *str != ' '  &&  *str != 9)
      ++str;
   while (*str  && (*str == ' ' || *str == 9))
      ++str;
   return (str);
}


char *
compile_av(av, start, end)
char **av;
{
   char *cstr;
   int i, len;

   len = 0;
   for (i = start; i < end; ++i)
      len += strlen(av[i]) + 1;
   cstr = malloc(len + 1);
   *cstr = '\0';
   for (i = start; i < end; ++i) {
      strcat (cstr, av[i]);
      strcat (cstr, " ");
   }
   return (cstr);
}



Free(ptr)
char *ptr;
{
   static char *old_ptr;

   if (old_ptr)
      free (old_ptr);
   old_ptr = ptr;
}

/*
 * Add new string to history (H_head, H_tail, H_len,
 *  S_histlen
 */

add_history(str)
char *str;
{
   register struct HIST *hist;

   while (H_len > S_histlen)
      del_history();
   hist = (struct HIST *)malloc (sizeof(struct HIST));
   if (H_head == NULL) {
      H_head = H_tail = hist;
      hist->next = NULL;
   } else {
      hist->next = H_head;
      H_head->prev = hist;
      H_head = hist;
   }
   hist->prev = NULL;
   hist->line = malloc (strlen(str) + 1);
   strcpy (hist->line, str);
   ++H_len;
}

del_history()
{
   if (H_tail) {
      --H_len;
      ++H_tail_base;
      free (H_tail->line);
      if (H_tail->prev) {
         H_tail = H_tail->prev;
         free (H_tail->next);
         H_tail->next = NULL;
      } else {
         free (H_tail);
         H_tail = H_head = NULL;
      }
   }
}

char *
get_history(ptr)
char *ptr;
{
   register struct HIST *hist;
   register int len;
   int mode = HM_REL;
   int num  = 1;
   char *str;

   if (ptr[1] >= '0' && ptr[1] <= '9') {
      mode = HM_ABS;
      num  = atoi(&ptr[1]);
      goto skip;
   }
   switch (ptr[1]) {
   case '!':
      break;
   case '-':
      num += atoi(&ptr[2]);
      break;
   default:
      mode = HM_STR;
      str  = ptr + 1;
      break;
   }
skip:
   switch (mode) {
   case HM_STR:
      len = strlen(str);
      for (hist = H_head; hist; hist = hist->next) {
         if (strncmp(hist->line, str, len) == 0)
            return (hist->line);
      }
      return (NULL);
   case HM_REL:
      for (hist = H_head; hist && num--; hist = hist->next);
      return ((hist) ? hist->line : NULL);
   case HM_ABS:
      len = H_tail_base;
      for (hist = H_tail; hist && len != num; hist = hist->prev, ++len);
      return ((hist) ? hist->line : NULL);
   }
   return (NULL);
}

replace_head(str1, str2, flag)
char *str1, *str2;
{
   char *str3;

   if (str1 == NULL)
      str1 = "";
   if (str2 == NULL) {
      str2 = "";
      flag = 0;
   }
   str3 = ((flag & (FL_EOC|FL_EOL)) == FL_EOC) ? ";" : " ";
   if (H_head) {
      free (H_head->line);
      H_head->line = malloc (strlen(str1)+strlen(str2)+2);
      strcpy (H_head->line, str1);
      strcat (H_head->line, str3);
      strcat (H_head->line, str2);
   }
}

perror(str)
char *str;
{
   struct PERROR *per = Perror;
   int err = IoErr();

   if (err) {
      for (; per->errstr; ++per) {
         if (per->errnum == err) {
            printf ("%s%s%s\n",
                  per->errstr,
                  (str) ? ": " : "",
                  (str) ? str : "");
            return (err);
         }
      }
      printf ("Unknown DOS error %d %s\n", err, (str) ? str : "");
   }
   return (err);
}

/*
 * Disk directory routines
 *
 * dptr = dopen(name, stat)
 *    struct DPTR *dptr;
 *    char *name;
 *    int *stat;
 *
 * dnext(dptr, name, stat)
 *    struct DPTR *dptr;
 *    char **name;
 *    int  *stat;
 *
 * dclose(dptr)                  -may be called with NULL without harm
 *
 * dopen() returns a struct DPTR, or NULL if the given file does not
 * exist.  stat will be set to 1 if the file is a directory.  If the
 * name is "", then the current directory is openned.
 *
 * dnext() returns 1 until there are no more entries.  The **name and
 * *stat are set.  *stat = 1 if the file is a directory.
 *
 * dclose() closes a directory channel.
 *
 */

struct DPTR *
dopen(name, stat)
char *name;
int *stat;
{
   struct DPTR *dp;
   int namelen, endslash = 0;

   namelen = strlen(name);
   if (namelen && name[namelen - 1] == '/') {
      name[namelen - 1] = '\0';
      endslash = 1;
   }
   *stat = 0;
   dp = (struct DPTR *)malloc(sizeof(struct DPTR));
   if (*name == '\0')
      dp->lock = DupLock (Clock);
   else
      dp->lock = Lock (name, ACCESS_READ);
   if (endslash)
      name[namelen - 1] = '/';
   if (dp->lock == NULL) {
      free (dp);
      return (NULL);
   }
   dp->fib = (struct FileInfoBlock *)
         AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC);
   if (!Examine (dp->lock, dp->fib)) {
      perror (name);
      dclose (dp);
      return (NULL);
   }
   if (dp->fib->fib_DirEntryType >= 0)
      *stat = 1;
   return (dp);
}

dnext(dp, pname, stat)
struct DPTR *dp;
char **pname;
int *stat;
{
   if (dp == NULL)
      return (0);
   if (ExNext (dp->lock, dp->fib)) {
      *stat = (dp->fib->fib_DirEntryType < 0) ? 0 : 1;
      *pname = dp->fib->fib_FileName;
      return (1);
   }
   return (0);
}


dclose(dp)
struct DPTR *dp;
{
   if (dp == NULL)
      return (1);
   if (dp->fib)
      FreeMem (dp->fib, sizeof(*dp->fib));
   if (dp->lock)
      UnLock (dp->lock);
   free (dp);
   return (1);
}

free_expand(av)
char **av;
{
   char **base = av;

   if (av) {
      while (*av) {
         free (*av);
         ++av;
      }
      free (base);
   }
}

/*
 * EXPAND(wild_name, pac)
 *    wild_name      - char * (example: "df0:*.c")
 *    pac            - int  *  will be set to # of arguments.
 *
 * Standalone, except in requires Clock to point to the Current-Directory
 * lock.
 */


char **
expand(base, pac)
char *base;
int *pac;
{
   char **eav = (char **)malloc (sizeof(char *));
   int  eleft, eac;

   char *ptr, *name;
   char *bname, *ename, *tail;
   int stat, scr;
   struct DPTR *dp;

   *pac = eleft = eac = 0;

   base = strcpy(malloc(strlen(base)+1), base);
   for (ptr = base; *ptr && *ptr != '?' && *ptr != '*'; ++ptr);
   for (; ptr >= base && !(*ptr == '/' || *ptr == ':'); --ptr);
   if (ptr < base) {
      bname = strcpy (malloc(1), "");
   } else {
      scr = ptr[1];
      ptr[1] = '\0';
      bname = strcpy (malloc(strlen(base)+1), base);
      ptr[1] = scr;
   }
   ename = ptr + 1;
   for (ptr = ename; *ptr && *ptr != '/'; ++ptr);
   scr = *ptr;
   *ptr = '\0';
   tail = (scr) ? ptr + 1 : NULL;

   if ((dp = dopen (bname, NULL, &stat)) == NULL  ||  stat == 0) {
      free (bname);
      free (base);
      free (eav);
      puts ("Could not open directory");
      return (NULL);
   }
   while (dnext (dp, &name, &stat)) {
      if (compare_ok(ename, name)) {
         if (tail) {
            int alt_ac;
            char *search, **alt_av, **scrav;
            struct FileLock *lock;

            if (!stat)           /* expect more dirs, but this not a dir */
               continue;
            lock = CurrentDir (Clock = dp->lock);
            search = malloc(strlen(name)+strlen(tail)+2);
            strcpy (search, name);
            strcat (search, "/");
            strcat (search, tail);
            scrav = alt_av = expand (search, &alt_ac);
            CurrentDir (Clock = lock);
            if (scrav) {
               while (*scrav) {
                  if (eleft < 2) {
                     char **scrav = (char **)malloc(sizeof(char *) * (eac + 10));
                     movmem (eav, scrav, sizeof(char *) * (eac + 1));
                     free (eav);
                     eav = scrav;
                     eleft = 10;
                  }
                  eav[eac] = malloc(strlen(bname)+strlen(*scrav)+1);
                  strcpy(eav[eac], bname);
                  strcat(eav[eac], *scrav);
                  free (*scrav);
                  ++scrav;
                  --eleft, ++eac;
               }
               free (alt_av);
            }
         } else {
            if (eleft < 2) {
               char **scrav = (char **)malloc(sizeof(char *) * (eac + 10));
               movmem (eav, scrav, sizeof(char *) * (eac + 1));
               free (eav);
               eav = scrav;
               eleft = 10;
            }
            eav[eac] = malloc (strlen(bname)+strlen(name)+1);
            eav[eac] = strcpy(eav[eac], bname);
            strcat(eav[eac], name);
            --eleft, ++eac;
         }
      }
   }
   dclose (dp);
   *pac = eac;
   eav[eac] = NULL;
   free (bname);
   free (base);
   if (eac)
      return (eav);
   free (eav);
   return (NULL);
}

/*
 * Compare a wild card name with a normal name
 */

#define MAXB   8

compare_ok(wild, name)
char *wild, *name;
{
   char *w = wild;
   char *n = name;
   char *back[MAXB][2];
   int  bi = 0;

   while (*n || *w) {
      switch (*w) {
      case '*':
         if (bi == MAXB) {
            puts ("Too many levels of '*'");
            return (0);
         }
         back[bi][0] = w;
         back[bi][1] = n;
         ++bi;
         ++w;
         continue;
goback:
         --bi;
         while (bi >= 0 && *back[bi][1] == '\0')
            --bi;
         if (bi < 0)
            return (0);
         w = back[bi][0] + 1;
         n = ++back[bi][1];
         ++bi;
         continue;
      case '?':
         if (!*n) {
            if (bi)
               goto goback;
            return (0);
         }
         break;
      default:
         if (*n != *w) {
            if (bi)
               goto goback;
            return (0);
         }
         break;
      }
      if (*n)  ++n;
      if (*w)  ++w;
   }
   return (1);
}




!Funky!Stuff!
fi # end of overwriting check
#	End of shell archive
exit 0