[mod.amiga.sources] SHAR 2 of 4

doc@pucc-j.UUCP (09/02/86)

Reply-To: dillon%cory.Berkeley.EDU@BERKELEY.EDU (Matt Dillon)

	This shar file contains the latest version of my shell.  See the
README and instructions.txt file within for more details.

				-Matt

#! /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:
#	README
#	comm1.c
#	comm2.c
#	execom.c
#	globals.c
#	instructions.txt
#	main.c
#	run.c
#	set.c
#	shell.h
#	sub.c
# This archive created: Wed Aug 27 19:51:38 1986
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'README'" '(464 characters)'
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
cat << \!Funky!Stuff! > 'README'

SHELL V 2.01   Matthew Dillon   COMPILATION

   -Use a 32 bit compiler
   -Use Astartup.obj (or equivalent) as your startup object file.
    Do NOT use Lstartup.obj.

   -Use the libraries (in this order):  MY.LIB AMIGA.LIB LC.LIB


      MY.LIB is my own library which you should have.  If not, mail
      via UUCP or ARPAnet and I'll give you a copy.

      LC.LIB is only in there because lattice uses a function in
      LC.LIB for long integer multiplies.


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

/*
 * COMM1.C
 *
 * Matthew Dillon, August 1986
 *
 *
 *
 */

#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_sleep()
{
   register int i;

   if (ac == 2) {
      i = atoi(av[1]);
      while (i > 0) {
         Delay (50*2);
         i -= 2;
         if (CHECKBREAK())
            break;
      }
   }
   return (1);
}


do_number()
{
   return (1);
}

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

   if (ac == 1) {
      while (Ogets(buf))
         Oputs(buf);
      return (1);
   }
   for (i = 1; i < ac; ++i) {
      if (fi = xopen (av[i], "r", 512)) {
         while (xgets (fi, buf, 256)) {
            Oputs(buf);
            if (CHECKBREAK())
               break;
         }
         xclose (fi);
      } else {
         printf ("could not open %s\n", av[i]);
      }
   }
   return (1);
}

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;
            fprintf (Cout, "Unit:%2ld  Errs:%3ld  Bytes: %-7ld Free: %-7ld\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);
               if (CHECKBREAK())
                  break;
            }
         } else {
            total += disp_entry(dp->fib);
         }
      }
      dclose (dp);
   }
   fprintf (Cout, "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";

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


do_quit()
{
   if (Src_stack) {
      Quit = 1;
      return(do_return());
   }
   main_exit (0);
}


do_echo(str)
char *str;
{
   register char *ptr;
   char nl = 1;

   for (ptr = str; *ptr && *ptr != ' '; ++ptr);
   if (*ptr == ' ')
      ++ptr;
   if (av[1] && strcmp (av[1], "-n") == 0) {
      nl = 0;
      ptr += 2;
      if (*ptr == ' ')
         ++ptr;
   }
   Write(Cout, ptr, strlen(ptr));
   if (nl)
      Oputs("");
   return (1);
}


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

   if (Src_stack == MAXSRC) {
      puts ("Too many source levels");
      return(-1);
   }
   fi = xopen (next_word(str), "r", 512);
   if (fi == 0) {
      printf ("Cannot open %s\n", next_word(str));
      return(-1);
   }
   ++H_stack;
   Src_pos[Src_stack] = 0;
   Src_base[Src_stack] = fi;
   ++Src_stack;
   while (xgets (fi, buf, 256)) {
      Src_pos[Src_stack - 1] += 1+strlen(buf);
      if (Verbose)
         puts(buf);
      exec_command (buf);
      if (CHECKBREAK())
         break;
   }
   --H_stack;
   --Src_stack;
   unset_level(LEVEL_LABEL + Src_stack);
   xclose (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) {
      Oputs (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;
{
   register 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 (Rename (av[1], av[2]))
      return (1);
   perror ("rm:");
   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;
   register int len = (av[1]) ? strlen(av[1]) : 0;

   for (hist = H_tail; hist; hist = hist->prev) {
      if (len == 0 || strncmp(av[1], hist->line, len) == 0) {
         fprintf (Cout, "%3ld ", i);
         Oputs (hist->line);
      }
      ++i;
      if (CHECKBREAK())
         break;
   }
   return (1);
}

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

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

   if (ffree)
      fprintf (Cout, "FAST memory:%10ld\n", ffree);
   fprintf (Cout, "CHIP memory:%10ld\n", cfree);
   fprintf (Cout, "Total -----:%5ld K\n", (ffree + cfree) >> 10);
   return (1);
}

/*
 * 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, *scr, *args;

   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);
   ptr = args = compile_av (av, cstart, cend);
   while (*ptr) {
      while (*ptr == ' ' || *ptr == 9)
         ++ptr;
      scr = ptr;
      if (*scr == '\0')
         break;
      while (*ptr && *ptr != ' ' && *ptr != 9)
         ++ptr;
      old = *ptr;
      *ptr = '\0';
      set_var (LEVEL_SET, vname, scr);
      if (CHECKBREAK())
         break;
      exec_command (cstr);
      *ptr = old;
   }
   --H_stack;
   free (args);
   free (cstr);
   unset_var (LEVEL_SET, vname);
   free (vname);
   return (1);
}


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

/*
 * MORE commands
 */

#include "shell.h"

do_return()
{
   if (Src_stack) {
      xseek (Src_base[Src_stack - 1], 0, 1);
      Rval = (ac < 2) ? 1 : atoi(av[1]);
      return (-1);
   } else {
      main_exit ((ac < 2) ? 1 : atoi(av[1]));
   }
}

/*
 * STRHEAD
 *
 * place a string into a variable removing everything after and including
 * the 'break' character or until a space is found in the string.
 *
 * strhead varname breakchar string
 *
 */

do_strhead()
{
   register char *str = av[3];
   char bc = *av[2];

   while (*str && *str != bc)
      ++str;
   *str = '\0';
   set_var (LEVEL_SET, av[1], av[3]);
}

do_strtail()
{
   register char *str = av[3];
   char bc = *av[2];

   while (*str && *str != bc)
      ++str;
   if (*str)
      ++str;
   set_var (LEVEL_SET, av[1], str);
}



/*
 * if A < B   <, >, =, <=, >=, !=, where A and B are either:
 * nothing
 * a string
 * a value (begins w/ number)
 */

do_if(garbage, com)
char *garbage;
{
   char *v1, *v2, *v3, result, num;
   int n1, n2;

   switch (com) {
   case 0:
      if (If_stack && If_base[If_stack - 1]) {
         If_base[If_stack++] = 1;
         break;
      }
      result = num = 0;
      if (ac <= 2) {       /* if $var; */
         if (ac == 1 || strlen(av[1]) == 0 || (strlen(av[1]) == 1 && *av[1] == ' '))
            goto do_result;
         result = 1;
         goto do_result;
      }
      if (ac != 4) {
         ierror(NULL, 500);
         break;
      }
      v1 = av[1]; v2 = av[2]; v3 = av[3];
      while (*v1 == ' ')
         ++v1;
      while (*v2 == ' ')
         ++v2;
      while (*v3 == ' ')
         ++v3;
      if (*v1 >= '0' && *v1 <= '9') {
         num = 1;
         n1 = atoi(v1);
         n2 = atoi(v3);
      }
      while (*v2) {
         switch (*v2++) {
         case '>':
            result |= (num) ? (n1 >  n2) : (strcmp(v1, v3) > 0);
            break;
         case '<':
            result |= (num) ? (n1 <  n2) : (strcmp(v1, v3) < 0);
            break;
         case '=':
            result |= (num) ? (n1 == n2) : (strcmp(v1, v3) ==0);
            break;
         default:
            ierror (NULL, 503);
            break;
         }
      }
do_result:
      If_base[If_stack++] = !result;
      break;
   case 1:
      if (If_stack > 1 && If_base[If_stack - 2])
         break;
      if (If_stack)
         If_base[If_stack - 1] ^= 1;
      break;
   case 2:
      if (If_stack)
         --If_stack;
      break;
   }
   Disable = (If_stack) ? If_base[If_stack - 1] : 0;
   return (1);
}

do_label()
{
   char aseek[32];

   if (Src_stack == 0) {
      ierror (NULL, 502);
      return (-1);
   }
   sprintf (aseek, "%ld %ld", Src_pos[Src_stack-1], If_stack);
   set_var (LEVEL_LABEL + Src_stack - 1, av[1], aseek);
   return (1);
}

do_goto()
{
   int new;
   long pos;
   char *lab;

   if (Src_stack == 0) {
      ierror (NULL, 502);
   } else {
      lab = get_var (LEVEL_LABEL + Src_stack - 1, av[1]);
      if (lab == NULL) {
         ierror (NULL, 501);
      } else {
         pos = atoi(lab);
         xseek (Src_base[Src_stack - 1], pos, -1);
         Src_pos[Src_stack - 1] = pos;
         new = atoi(next_word(lab));
         for (; If_stack < new; ++If_stack)
            If_base[If_stack] = 0;
         If_stack = new;
      }
   }
   return (-1);      /* Don't execute reset of this line */
}


do_failat()
{
   Faillevel = 1;
   Oputs ("NOT IMPLIMENTED YET");
}

do_checkbrk()
{
   Oputs ("NOT IMPLIMENTED YET");
}

do_inc(garbage, com)
char *garbage;
{
   char *var;
   char num[32];

   if (ac == 3)
      com = atoi(av[2]);
   var = get_var (LEVEL_SET, av[1]);
   if (var) {
      sprintf (num, "%ld", atoi(var)+com);
      set_var (LEVEL_SET, av[1], num);
   }
}

do_input()
{
   char in[256];

   if (Ogets(in))
      set_var (LEVEL_SET, av[1], in);
   return (1);
}

do_ver()
{
   Oputs (VERSION);
   return (1);
}


!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'execom.c'" '(14496 characters)'
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, 10 August 1986
 *
 *    Finally re-written.
 *
 */

#include "shell.h"

#define F_EXACT 0
#define F_ABBR  1

#define ST_COND   0x01
#define ST_NAME   0x02


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

extern char *format_insert_string();
extern char *mpush(), *exarg();

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(), do_return(), do_if(), do_label(), do_goto();
extern int do_failat(), do_checkbrk(), do_inc();
extern int do_input(), do_ver(), do_sleep(), do_help();
extern int do_strhead(), do_strtail();

static struct COMMAND Command[] = {
   do_run      , 0,  ST_NAME,    0 ,   "\001",
   do_number   , 0,  0,          0 ,   "\001",
   do_quit     , 0,  0,          0 ,   "quit",
   do_set_var  , 0,  0, LEVEL_SET  ,   "set",
   do_unset_var, 0,  0, LEVEL_SET  ,   "unset",
   do_set_var  , 0,  0, LEVEL_ALIAS,   "alias",
   do_unset_var, 0,  0, LEVEL_ALIAS,   "unalias",
   do_echo     , 0,  0,          0 ,   "echo",
   do_source   , 0,  0,          0 ,   "source",
   do_mv       , 2,  0,          0 ,   "mv",
   do_cd       , 0,  0,          0 ,   "cd",
   do_rm       , 0,  0,          0 ,   "rm",
   do_mkdir    , 0,  0,          0 ,   "mkdir",
   do_history  , 0,  0,          0 ,   "history",
   do_mem      , 0,  0,          0 ,   "mem",
   do_cat      , 0,  0,          0 ,   "cat",
   do_dir      , 0,  0,          0 ,   "dir",
   do_dir      , 0,  0,         -1 ,   "devinfo",
   do_foreach  , 3,  0,          0 ,   "foreach",
   do_return   , 0,  0,          0 ,   "return",
   do_if       , 1,  ST_COND,    0 ,   "if",
   do_if       , 0,  ST_COND,    1 ,   "else",
   do_if       , 0,  ST_COND,    2 ,   "endif",
   do_label    , 1,  ST_COND,    0 ,   "label",
   do_goto     , 1,  0,          0 ,   "goto",
   do_failat   , 1,  0,          0 ,   "failat",
   do_checkbrk , 0,  0,          0 ,   "checkbreak",
   do_strhead  , 3,  0,          0 ,   "strhead",
   do_strtail  , 3,  0,          0 ,   "strtail",
   do_inc      , 1,  0,          1 ,   "inc",
   do_inc      , 1,  0,          -1,   "dec",
   do_input    , 1,  0,          0,    "input",
   do_ver      , 0,  0,          0,    "version",
   do_sleep    , 0,  0,          0,    "sleep",
   do_help     , 0,  0,          0,    "help",
   NULL        , 0,  0,          0 ,   NULL
};


static unsigned char elast;          /* last end delimeter */
static char Cin_ispipe, Cout_ispipe;

exec_command(base)
char *base;
{
   register char *scr;
   register int i;

   if (!H_stack)
      add_history(base);
   scr = malloc((strlen(base) << 2) + 2);    /* 4X */
   preformat(base, scr);
   i = fcomm(scr, 1);
   return ((i) ? -1 : 1);
}

isalphanum(c)
{
   if (c >= '0' && c <= '9')
      return (1);
   if (c >= 'a' && c <= 'z')
      return (1);
   if (c >= 'A' && c <= 'Z')
      return (1);
   if (c == '_')
      return (1);
   return (0);
}

preformat(s, d)
register char *s, *d;
{
   register int si, di, qm;

   si = di = qm = 0;
   while (s[si] == ' ' || s[si] == 9)
      ++si;
   while (s[si]) {
      if (qm && s[si] != '\"' && s[si] != '\\') {
         d[di++] = s[si++] | 0x80;
         continue;
      }
      switch (s[si]) {
      case ' ':
      case 9:
         d[di++] = ' ';
         while (s[si] == ' ' || s[si] == 9)
            ++si;
         if (s[si] == 0 || s[si] == '|' || s[si] == ';')
            --di;
         break;
      case '*':
      case '?':
         d[di++] = 0x80;
      case '!':
         d[di++] = s[si++];
         break;
      case ';':
      case '|':
         d[di++] = s[si++];
         while (s[si] == ' ' || s[si] == 9)
            ++si;
         break;
      case '\\':
         d[di++] = s[++si] | 0x80;
         if (s[si]) ++si;
         break;
      case '\"':
         qm = 1 - qm;
         ++si;
         break;
      case '^':
         d[di++] = s[++si] & 0x1F;
         if (s[si]) ++si;
         break;
      case '$':         /* search end of var name and place false space */
         d[di++] = 0x80;
         d[di++] = s[si++];
         while (isalphanum(s[si]))
            d[di++] = s[si++];
         d[di++] = 0x80;
         break;
      default:
         d[di++] = s[si++];
         break;
      }
   }
   d[di++] = 0;
   d[di]   = 0;
   if (Debug & 0x01) {
      printf ("PREFORMAT: %ld :%s:\n", strlen(d), d);
   }
}

/*
 * process formatted string.  ' ' is the delimeter.
 *
 *    0: check '\0': no more, stop, done.
 *    1: check $.     if so, extract, format, insert
 *    2: check alias. if so, extract, format, insert. goto 1
 *    3: check history or substitution, extract, format, insert. goto 1
 *
 *    4: assume first element now internal or disk based command.
 *
 *    5: extract each ' ' or 0x80 delimited argument and process, placing
 *       in av[] list (except 0x80 args appended).  check in order:
 *
 *             '$'         insert string straight
 *             '>'         setup stdout
 *             '<'         setup stdin
 *             '*' or '?'  do directory search and insert as separate args.
 *
 *             ';' 0 '|'   end of command.  if '|' setup stdout
 *                          -execute command, fix stdin and out (|) sets
 *                           up stdin for next guy.
 */


fcomm(str, freeok)
register char *str;
{
   register char *istr;
   char *nextstr;
   char *command;
   char *pend_alias = NULL;      /* pending alias                 */
   char err = 0;

   mpush_base();
   if (*str == 0)
      goto done1;
step1:
   if (*str == '$') {
      if (istr = get_var (LEVEL_SET, str + 1))
         str = format_insert_string(str, istr, &freeok);
   }
   istr = get_var (LEVEL_ALIAS, str);
   if (istr) {
      if (*istr == '%') {
         pend_alias = istr;
      } else {
         str = format_insert_string(str, istr, &freeok);
         goto step1;
      }
   }
   if (*str == '!') {
      istr = get_history(str);
      replace_head(istr);
      str = format_insert_string(str, istr, &freeok);
      goto step1;
   }
   nextstr = str;
   command = exarg(&nextstr);
   if (*command == 0)
      goto done0;
   if (pend_alias == 0) {
      register int ccno;
      ccno = find_command(command);
      if (Command[ccno].stat & ST_COND)
         goto skipgood;
   }
   if (Disable)
      goto done0;
skipgood:
   {
      register char *arg, *ptr, *scr;
      short redir;
      short doexpand;
      short cont;
      short inc;

      ac = 1;
      av[0] = command;
step5:                                          /* ac = nextac */
      if (!elast || elast == ';' || elast == '|')
         goto stepdone;

      av[ac] = NULL;
      cont = 1;
      doexpand = redir = inc = 0;

      while (cont && elast) {
         ptr = exarg(&nextstr);
         inc = 1;
         arg = "";
         cont = (elast == 0x80);
         switch (*ptr) {
         case '<':
            redir = -2;
         case '>':
            ++redir;
            arg = ptr + 1;
            cont = 1;
            break;
         case '$':
            if ((arg = get_var(LEVEL_SET, ptr + 1)) == NULL)
               arg = ptr;
            break;
         case '*':
         case '?':
            doexpand = 1;
            arg = ptr;
            break;
         default:
            arg = ptr;
            break;
         }

         /* Append arg to av[ac] */

         for (scr = arg; *scr; ++scr)
            *scr &= 0x7F;
         if (av[ac]) {
            register char *old = av[ac];
            av[ac] = mpush(strlen(arg)+1+strlen(av[ac]));
            strcpy(av[ac], old);
            strcat(av[ac], arg);
         } else {
            av[ac] = mpush(strlen(arg)+1);
            strcpy(av[ac], arg);
         }
         if (elast != 0x80)
            break;
      }

      /* process expansion */

      if (doexpand) {
         char **eav, **ebase;
         int eac;

         eav = ebase = expand(av[ac], &eac);
         if (eav) {
            inc = 0;
            if (ac + eac + 2 > MAXAV) {
               ierror (NULL, 506);
               err = 1;
            } else {
               for (; eac; --eac, ++eav)
                  av[ac++] = strcpy(mpush(strlen(*eav)+1), *eav);
            }
            free_expand (ebase);
         } else {
            puts ("null expansion");
            err = 1;
         }
      }

      /* process redirection  */

      if (redir && !err) {
         register char *file = (doexpand) ? av[--ac] : av[ac];

         if (redir < 0)
            Cin_name = file;
         else
            Cout_name = file;
         inc = 0;
      }

      /* check elast for space */

      if (inc) {
         ++ac;
         if (ac + 2 > MAXAV) {
            ierror (NULL, 506);
            err = 1;                /* error condition */
            elast = 0;              /* don't process any more arguemnts */
         }
      }
      if (elast == ' ')
         goto step5;
   }
stepdone:
   av[ac] = NULL;

   /* process pipes via files */

   if (elast == '|' && !err) {
      static int which;             /* 0 or 1 in case of multiple pipes */
      which = 1 - which;
      Cout_name = (which) ? Pipe1 : Pipe2;
      Cout_ispipe = 1;
   }

   /* DO COMMAND HERE! */

   if (err)
      goto done0;

   {
      register int i, len;
      char save_elast;
      char *avline;

      save_elast = elast;
      for (i = len = 0; i < ac; ++i)
         len += strlen(av[i]) + 1;
      avline = malloc(len+1);
      for (len = 0, i = ((pend_alias) ? 1 : 0); i < ac; ++i) {
         if (Debug & 0x02) printf ("AV[%2ld] %ld :%s:\n", i, strlen(av[i]), av[i]);
         strcpy(avline + len, av[i]);
         len += strlen(av[i]);
         if (i + 1 < ac)
            avline[len++] = ' ';
      }
      avline[len] = 0;
      if (pend_alias) {                               /* special % alias */
         register char *ptr, *scr;
         for (ptr = pend_alias; *ptr && *ptr != ' '; ++ptr);
         set_var (LEVEL_SET, pend_alias + 1, avline);
         free (avline);

         scr = malloc((strlen(ptr) << 2) + 2);
         preformat (ptr, scr);
         fcomm (scr, 1);
         unset_var (LEVEL_SET, pend_alias + 1);
      } else {                                        /* normal command  */
         register int ccno;
         register long cin, cout;

         ccno = find_command (command);
         if ((Command[ccno].stat & ST_NAME) == 0) {
            if (Cin_name) {
               cin = Cin;
               Cin = Open(Cin_name, 1005);
               if (Cin == 0) {
                  ierror (NULL, 504);
                  err = 1;
                  Cin = cin;
                  Cin_name = NULL;
               }
            }
            if (Cout_name) {
               cout = Cout;
               Cout = Open(Cout_name, 1006);
               if (Cout == NULL) {
                  err = 1;
                  ierror (NULL, 504);
                  Cout = cout;
                  Cout_name = NULL;
               }
            }
         }
         if (ac < Command[ccno].minargs + 1) {
            ierror (NULL, 500);
            err = 1;
         } else {
            i = (*Command[ccno].func)(avline, Command[ccno].val);
            err = (i < 0);
         }
         free (avline);
         if ((Command[ccno].stat & ST_NAME) == 0) {
            if (Cin_name) {
               Close(Cin);
               Cin = cin;
            }
            if (Cout_name) {
               Close(Cout);
               Cout = cout;
            }
         }
      }
      if (Cin_ispipe && Cin_name)
         DeleteFile(Cin_name);
      if (Cout_ispipe) {
         Cin_name = Cout_name;         /* ok to assign.. static name */
         Cin_ispipe = 1;
      } else {
         Cin_name = NULL;
      }
      Cout_name = NULL;
      Cout_ispipe = 0;
      elast = save_elast;
   }
   mpop_tobase();                      /* free arguments   */
   mpush_base();                       /* push dummy base  */

done0:
   if (elast != 0 && !err)             /* done? or more? */
      err = fcomm(nextstr, 0);         /* do next command ';' or '|' sep. */

   if (Cin_name)                       /* pipe segment complete   */
      DeleteFile(Cin_name);
   Cin_name = NULL;
   Cin_ispipe = 0;

done1:
   mpop_tobase();
   if (freeok)
      free(str);
   return ((int)err);                  /* TRUE = error occured    */
}


char *
exarg(ptr)
unsigned char **ptr;
{
   register unsigned char *end;
   register unsigned char *start;

   start = end = *ptr;
   while (*end && *end != 0x80 && *end != ';' && *end != '|' && *end != ' ')
      ++end;
   elast = *end;
   *end = '\0';
   *ptr = end + 1;
   return ((char *)start);
}

static char **Mlist;

mpush_base()
{
   char *str;

   str = malloc(5);
   *(char ***)str = Mlist;
   str[4] = 0;
   Mlist = (char **)str;
}

char *
mpush(bytes)
{
   char *str;

   str = malloc(5 + bytes);
   *(char ***)str = Mlist;
   str[4] = 1;
   Mlist = (char **)str;
   return (str + 5);
}

mpop_tobase()
{
   register char *next;
   while (Mlist) {
      next = *Mlist;
      if (((char *)Mlist)[4] == 0) {
         free (Mlist);
         Mlist = (char **)next;
         break;
      }
      free (Mlist);
      Mlist = (char **)next;
   }
}


/*
 * Insert 'from' string in front of 'str' while deleting the
 * first entry in 'str'.  if freeok is set, then 'str' will be
 * free'd
 */



char *
format_insert_string(str, from, freeok)
char *str;
char *from;
int *freeok;
{
   register char *new1, *new2;
   register unsigned char *strskip;
   int len;

   for (strskip = str; *strskip && *strskip != ' ' && *strskip != ';' && *strskip != '|' && *strskip != 0x80; ++strskip);
   len = strlen(from);
   new1 = malloc((len << 2) + 2);
   preformat(from, new1);
   len = strlen(new1) + strlen(strskip);
   new2 = malloc(len+2);
   strcpy(new2, new1);
   strcat(new2, strskip);
   new2[len+1] = 0;
   free (new1);
   if (*freeok)
      free (str);
   *freeok = 1;
   return (new2);
}

find_command(str)
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)
         return (i);
   }
   return (0);
}

do_help()
{
   register struct COMMAND *com;

   for (com = &Command[2]; com->func; ++com)
      fprintf (Cout, "%s ", com->name);
   Oputs ("");
}

!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'globals.c'" '(1912 characters)'
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",
   105,  "task table full",
   120,  "argument line invalid or too long",
   121,  "file is not an object module",
   122,  "invalid resident library during load",
   201,  "no default directory",
   202,  "object in use",
   203,  "object already exists",
   204,  "directory not found",
   205,  "object not found",
   206,  "bad stream name",
   207,  "object too large",
   209,  "action not known",
   210,  "invalid stream component name",
   211,  "invalid object lock",
   212,  "object not of required type",
   213,  "disk not validated",
   214,  "disk write protected",
   215,  "rename across devices",
   216,  "directory not empty",
   217,  "too many levels",
   218,  "device not mounted",
   219,  "seek error",
   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",
   232,  "no more entries in directory",

   /* custom error messages */

   500,  "bad arguments",
   501,  "label not found",
   502,  "must be within source file",
   503,  "Syntax Error",
   504,  "redirection error",
   505,  "pipe error",
   506,  "too many arguments",
     0,  NULL
};

char  *av[MAXAV];
int   Src_base[MAXSRC];
long  Src_pos[MAXSRC];
char  If_base[MAXIF];
int   H_len, H_tail_base, H_stack;
int   Src_stack, If_stack;
int   ac;
int   Debug, Rval, Disable, Faillevel, Verbose;
int   Quit;
int   Cout, Cin;              /* current input and output file handles */
int   Uniq;                   /* unique value */
char  *Cin_name, *Cout_name;  /* redirection input/output name or NULL */
char  *Pipe1, *Pipe2;         /* the two pipe temp. files              */

int   S_histlen = 20;


!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'instructions.txt'" '(11840 characters)'
if test -f 'instructions.txt'
then
	echo shar: "will not over-write existing file 'instructions.txt'"
else
cat << \!Funky!Stuff! > 'instructions.txt'

INSTRUCTIONS FOR SHELL 2.01


      (A)   Compiling
      (B)   Overview
      (C)   Quicky tech notes on implimentation.

      (D)   Command pre-processor
      (E)   Command-list
      (F)   special SET variables

      (G)   example .login file.


(A) COMPILING:

   Compile the shell with a 32 bit compiler.  16 bit compilers will not
   work.  Use the Astartup.obj startup module, and the three libraries:

      MY.LIB AMIGA.LIB LC.LIB

   NOTE the order in which the libraries occur. LC.LIB is only in there
   because the idiots at lattice use a call to do 32-bit integer multiplies.

   MY.LIB is my own library which has been posted (but feel free to ask
   me to send it to you if you haven't got it)

   lc1 -i$incdir/ -i$incdir/lattice/ $y
   lc2 -s -v $y

      where $incdir is the include directory for C, and '$y' stands for
      each .C file.  At this point you have nothing but object modules and
      must now link them together:

   alink faster Astartup.obj+main.o+...+run.o library $libdir/my.lib+$libdir/amiga.lib+$libs+$libdir/lc.lib to ram:shell

      main.o+...+run.o stands for all the object modules.  Note that my
      library 'my.lib' must exist.  Note also that Lattice's library is at
      the end.  $libdir stands for where your C libraries are.
      Astartup.obj is Lattice's startup module which doesn't use Lattice's
      library (thank god!).  As I said above, the only routine used from
      LC.LIB is their long multiply routine.

   Executable Size should be around 24K.


(B) OVERVIEW:

   overview of the major features:

   -simple history
   -redirection
   -piping
   -aliases
   -variables & variable handling
   -file name expansion via '?' and '*'
   -conditionals
   -source files  (w/ gotos and labels)
   -many built in commands to speed things up


   changes from V1.02:

   -you can now redirect through internal commands as well as external.
   -piping has been added.  You can pipe from/to internal commands.
   -you can now embed strings ( charlie/$var/ben ), making the shell more
    flexible.
   -history substitution displays line to be executed.
   -verbose mode for debugging source files
   -break handled a little better (still can't send break to external
    commands)
   -some new commands



   sorry, I still havent figured out how to get the return value from
   external commands.


(C) QUICK TECH NOTES:

   PIPES have been implimented using temporary RAM: files.  Thus, you
   should be careful when specifying a 'ram:*' expansion as it might
   include the temp. files.  These files are deleted on completion of
   the pipe segment.

   My favorite new feature is the fact that you can now redirect to and
   from, and pipe internal commands.  'echo charlie >ram:x', for
   instance.  Another favorite:

      echo "echo mem | shell" | shell

   To accomplish these new features, I completely re-wrote the command
   parser in execom.c


(D) COMMAND LINE PRE-PROCESSING:

   preprocessing is done on the command line before it is passed on to
   an internal or external routine:

   ^c       where c is a character is converted to that control character.
            Thus, say '^l' for control-l.

   $name    where name is a variable name.  Variable names can consist of
            0-9, a-z, A-Z, and underscore (_).  The contents of the
            specified variable is used.  If the variable doesn't exist,
            the specifier is used.  That is, if the variable 'i' contains
            'charlie', then '$i' -> 'charlie'.  If the variable 'i' doesn't
            exist, then '$i'->'$i' .

   ;        delimits commands.   echo charlie ; echo ben.

   ' '      (a space). delimits arguments.

   "string" a quoted string.  For instance, if you want to echo five spaces
            and an 'a':

            echo      a       -> a
            echo "    a"      ->      a

   \c       overide the meaning of special characters.  '\^a' is a
            circumflex and an a rather than control-a.  To get a backslash,
            you must say '\\'.

   >file    specify output redirection.  All output from the command is
            placed in the specified file.

   <file    specify input redirection.  The command takes input from the
            file rather than the keyboard (note: not all commands require
            input).  It makes no sense to say  'echo <charlie' since
            the 'echo' command only outputs its arguments.

   |        PIPE specifier.  The output from the command on the left becomes
            the input to the command on the right.  The current SHELL
            implimentation uses temporary files to store the data.

   !!       execute the previously executed command.
   !nn      (nn is a number).  Insert the history command numbered n (see
            the HISTORY command)
   !partial search backwards through the history list for a command which
            looks the same as 'partial', and execute it.



(E)  COMMAND LIST:

   The first argument is the command-name... if it doesn't exist in the
   list below and isn't an alias, it is assumed to be an external (disk)
   command.

   NOTE: Many CLI specific external commands, such as 'path', and 'cd' will
   have no effect on the shell.  However, you can execute these commands
   from the initial CLI (startup script) BEFORE you run the shell.


   HELP

      simply displays all the available commands.


   QUIT

      quit my SHELL (awww!).  End, El-Zappo, Kapow. Done, Finis


   SET
   SET name
   SET name string

      The first method lists all current variable settings.
      The second method lists the setting for that particular variable,
      or creates the variable if it doesn't exist (to "")
      The last method sets a variable to a string.


   UNSET name name name....

      unset one or more variables.  Deletes them entirely.


   ALIAS
   ALIAS name
   ALIAS name string

      same as SET, but applies to the alias list.  You can alias a single
      name to a set of commands.  For instance:

      alias hi "echo a; echo b"

      then you can simply say 'hi'.  Aliases come in two forms the second
      form allows you to place the arguments after an alias in a variable
      for retrieval:

      alias xxx "%i echo this $i is a test"

      % xxx charlie
      this charlie is a test

      The rest of the command line is placed in the specified variable
      for the duration of the alias.  This is especially useful when used
      in conjunction with the 'FOREACH' command.


   UNALIAS name name name...

      delete aliases..


   ECHO string
   ECHO -n string

      echo the string to the screen.  If '-n' is specified, no newline is
      output.


   STRHEAD  varname breakchar string

      remove everything after and including the breakchar in 'string' and
      place in variable 'varname':

         % strhead j . aaa.bbb
         % echo $j
         aaa
         %


   STRTAIL  varname breakchar string

      remove everything before and including the breakchar in 'string' and
      place in variable 'varname':

         % strtail j . aaa.bbb
         % echo $j
         bbb
         %


   SOURCE file

      execute commands from a file.  You can create SHELL programs in
      a file and then execute them with this command.  Source'd files
      have the added advantage that you can have loops in your command
      files (see GOTO and LABEL)


   MV from to

      Exactly the Rename() call.  Allows you to rename files or move them
      around within a disk.


   CD ..
   CD path

      Change your current working directory.  You may specify '..' to go
      back one directory (this is a CD specific feature, and does not
      work with normal path specifications).


   RM file file file...

      DeleteFile().  Remove the specified files.


   MKDIR name name name...

      create the following directories.


   HISTORY [partial string]

      Displays the enumerated history list.  The size of the list is
      controlled by the _history variable.  If you specify a partial-
      string, only those entries matching that string are displayed.


   MEM

      Display current memory statistics.


   CAT [file file....]

      Type the specified files onto the screen.  If no file is specified,
      STDIN in used.


   DIR [path path ... ]

      Get a directory listing of the current directory or specified
      directories.


   DEVINFO [device: device:... ]

      Display Device statistics for the current device (CD base), or
      specified devices.


   FOREACH varname ( strings ) command

      'strings' is broken up into arguments.  Each argument is placed in
      the variable 'varname' in turn and 'command' executed.  To execute
      multiple commands, place them in quotes:

      % foreach i ( a b c d ) "echo -n $i;echo \" ha\""
      a ha
      b ha
      c ha
      d ha


   RETURN [value]

      return from a source file.  The rest of the source file is
      discarded.  Currently, the optional value is thrown away.


   IF argument conditional argument ;
   IF argument

      If a single argument is something to another argument.  Conditional
      clauses allowed:

      <, >, =, and combinations (wire or).  Thus <> is not-equal, >=
      larger or equal, etc...

      If the left argument is numeric, both arguments are treated as
      numeric.

      usually the argument is either a constant or a variable ($varname).

      The second form if IF is conditional on the existance of the argument.
      If the argument is a "" string, then false , else TRUE.


   ELSE ;

      else clause.


   ENDIF ;

      the end of an if statement.


   LABEL name

      create a program label right here.



   GOTO label

      goto the specified label name.  You can only use this command from a
      source file.



   FAILAT
   CHECKBREAK

      not implimented yet.


   DEC var
   INC var

      decrement or increment the numerical equivalent of the variable and
      place the ascii-string result back into that variable.



   INPUT varname

      input from STDIN (or a redirection, or a pipe) to a variable.  The
      next input line is placed in the variable.


   VER

      display my name and the version number.


   SLEEP timeout

      Sleep for 'timeout' seconds.



(F) SPECIAL SET VARIABLES

   _prompt
         This variable is set to the command you wish executed that will
         create your prompt.

   _history
         This variable is set to a numerical value, and specifies how far
         back your history should extend.

   _debug
         Debug mode... use it if you dare.

   _verbose
         Verbose mode (for source files).  display commands as they are
         executed.



(G) EXAMPLE .login file.

   from a CLI or the startup-script say 'SHELL filename'.  That file
   is sourced first.  thus, 'SHELL .login' will set up your favorite
   aliases:

   ------------------------------ example source file -----------------

echo "shells, Matt"
alias l     "%var if $var;echo $var;else;echo *;endif"
alias c     "echo ^l"
alias cc    "assign c: cb:c  ;cd ram:"
alias wb    "assign c: sys:c ;cd ram:"
alias ram   "cp c:run ram:; cp c:assign ram:; cp c:cp ram:; assign c: ram:"
alias ram2  "assign c: ram:"
alias ed    "run ED"
alias c1    "%z foreach y ( $z ) \"echo $y;lc1 -o$temp -i$incdir/ -i$incdir/lattice/ $y\";echo DONE"
alias c2    "%z foreach y ( $z ) \"echo $y;lc2 -s -v $temp$y\";echo DONE"
alias ld    "%var alink faster $libdir/lstartup.obj+$var library $libdir/lc.lib+$libdir/amiga.lib to $dest"
alias lds   "%var alink faster $libdir/Astartup.obj+$var library $libdir/my.lib+$libdir/amiga.lib+$libs to $dest"
set dest ram:a
set temp ram:
set libdir cb:clib
set incdir cb:include
set libs $libdir/lc.lib
cd ram:

   --------------------------------------------------------------------
!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'main.c'" '(1465 characters)'
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"
#include <libraries/dosextens.h>

extern long SetSignal();

char Inline[256];

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

   check32();
   init_vars();
   init();
   for (i = 1; i < argc; ++i) {
      strcpy (Inline, "source ");
      strcat (Inline, argv[i]);
      do_source (Inline, 0);
   }
   for (;;) {
      if ((prompt = get_var (LEVEL_SET, V_PROMPT)) == NULL)
         prompt = "echo -n \"% \"";
      if (CHECKBREAK()) {
         while (WaitForChar(Input(), 1))
            gets(Inline);
      }
      ++H_stack;
      exec_command (prompt);
      --H_stack;
      if (Quit || !gets(Inline))
         main_exit(0);
      breakreset();
      if (*Inline)
         exec_command(Inline);
   }
}

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

init()
{
   static char pipe1[32], pipe2[32];

   Cin = Input();
   Cout= Output();
   Uniq= FindTask(0);
   Pipe1 = pipe1;
   Pipe2 = pipe2;
   sprintf (pipe1, "ram:pipe1_%ld", Uniq);
   sprintf (pipe2, "ram:pipe2_%ld", Uniq);
}


main_exit(n)
{
   free_memory();
   exit (n);
}

breakcheck()
{
   if (SetSignal(0,0) & SIGBREAKF_CTRL_C)
      return (1);
   else
      return (0);
}

breakreset()
{
   SetSignal(0, SIGBREAKF_CTRL_C);
}


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

/*
 * RUN.C
 *
 * Matthew Dillon
 *
 */

#include "shell.h"

do_run(str)
char *str;
{
   register char *ptr, *scr;
   register char *cin_name, *cout_name;
   register int i;

   cin_name = (Cin_name) ? Cin_name : "*";
   cout_name= (Cout_name)? Cout_name: "*";

   ptr = malloc(strlen(str) + strlen(cin_name) + strlen(cout_name) + 10);
   for (scr = str; *scr && *scr != ' '; ++scr);
   i = scr - str;          /* str[i] is a space or \0 */
   bmov (str, ptr, i);
   ptr[i++] = ' ';
   ptr[i++] = '<';
   bmov (cin_name, ptr + i, strlen(cin_name));
   i += strlen(cin_name);
   ptr[i++] = ' ';
   ptr[i++] = '>';
   bmov (cout_name, ptr + i, strlen(cout_name));
   i += strlen(cout_name);
   strcpy (ptr + i, scr);
   i = Execute (ptr, 0, 0);
   free (ptr);
   return ((i) ? 1 : -1);
}


!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'set.c'" '(3549 characters)'
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, July 1986
 *
 */

#include "shell.h"
#define MAXLEVELS (3 + MAXSRC)

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;
   register int len;

   for (len = 0; isalphanum(name[len]); ++len);
   while (base != NULL) {
      if (strlen(base->name) == len && strncmp (name, base->name, len) == 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 (len + 1);
   bmov (name, base->name, len);
   base->name[len] = 0;
   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];
   register unsigned char *scr;
   register int len;

   for (scr = name; *scr && *scr != 0x80 && *scr != ' ' && *scr != ';' && *scr != '|'; ++scr);
   len = scr - name;

   while (base != NULL) {
      if (strlen(base->name) == len && strncmp (name, base->name, len) == 0)
         return (base->text);
      base = base->next;
   }
   return (NULL);
}

unset_level(level)
{
   register struct MASTER *base = Mbase[level];

   while (base) {
      Free (base->name);
      Free (base->text);
      Free (base);
      base = base->next;
   }
   Mbase[level] = NULL;
}

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

   for (len = 0; isalphanum(name[len]); ++len);
   while (base) {
      if (strlen(base->name) == len && strncmp (name, base->name, len) == 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;
{
   register 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) {
         fprintf (Cout, "%-10s ", base->name);
         Oputs (base->text);
         base = base->next;
      }
      return (1);
   }
   if (ac == 2) {
      str = get_var (level, av[1]);
      if (str) {
         fprintf (Cout, "%-10s ", av[1]);
         Oputs(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;
      Debug     = (str = get_var(LEVEL_SET, V_DEBUG))  ? atoi(str) : 0;
      Verbose   = (get_var(LEVEL_SET, V_VERBOSE)) ? 1 : 0;
      if (S_histlen < 2)   S_histlen = 2;
   }
   return (1);
}


!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'shell.h'" '(2220 characters)'
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
 *
 */

#include <exec/types.h>

#define MAXAV        128            /* Max. # arguments             */
#define MAXSRC       5              /* Max. # of source file levels */
#define MAXIF        10             /* Max. # of if levels          */

#define LEVEL_SET    0
#define LEVEL_ALIAS  1
#define LEVEL_LABEL  2

#define M_RESET      0
#define M_CONT       1

#define V_PROMPT     "_prompt"      /* Special variable names */
#define V_HIST       "_history"
#define V_DEBUG      "_debug"
#define V_VERBOSE    "_verbose"

#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)

#define VERSION   "V2.01  (c)1986 Matthew Dillon."

#ifndef NULL
#define NULL 0L
#endif

#define CHECKBREAK() ( breakcheck() ? (printf("^C\n"),1) : 0)


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(), *strcpy(), *strcat(), *AllocMem(), *gets(), *Ogets();
extern char **expand();

extern char *av[];
extern char *Current;
extern int  H_len, H_tail_base, H_stack;
extern int  Src_stack, If_stack;
extern int  ac;
extern int  Debug, Rval, Verbose, Disable, Faillevel, Quit;
extern int  S_histlen, Uniq;
extern int  Cin, Cout;
extern char *Cin_name, *Cout_name;
extern char *Pipe1, *Pipe2;

extern int  Src_base[MAXSRC];
extern long Src_pos[MAXSRC];
extern char If_base[MAXIF];


!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'sub.c'" '(10083 characters)'
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;
   char *result = NULL;

   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) {
            result = hist->line;
            break;
         }
      }
      break;
   case HM_REL:
      for (hist = H_head; hist && num--; hist = hist->next);
      if (hist)
         result = hist->line;
      break;
   case HM_ABS:
      len = H_tail_base;
      for (hist = H_tail; hist && len != num; hist = hist->prev, ++len);
      if (hist)
         result = hist->line;
      break;
   }
   if (result)
      puts (result);
   return (result);
}


replace_head(str)
char *str;
{
   if (str == NULL)
      str = "";
   if (H_head) {
      free (H_head->line);
      H_head->line = malloc (strlen(str)+1);
      strcpy (H_head->line, str);
   }
}

perror(str)
char *str;
{
   ierror(str, IoErr());
}

ierror(str, err)
register char *str;
{
   register struct PERROR *per = Perror;

   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 %ld %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, &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));
                     bmov (eav, scrav, (eac + 1) << 2);
                     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));
               bmov (eav, scrav, (eac + 1) << 2);
               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);
}


Oputs(str)
char *str;
{
   Write (Cout, str, strlen(str));
   Write (Cout, "\n", 1);
}

char *
Ogets(str)
char *str;
{
   register int i = 0;

   while (Read(Cin, str + i, 1) == 1) {
      if (str[i] == '\n') {
         str[i] = 0;
         return (str);
      }
      if (++i == 255) {
         str[i] = 0;
         return (str);
      }
   }
   return (NULL);
}

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