[comp.sources.amiga] Shell 2.06M sources

doc@s.cc.purdue.edu.UUCP (06/08/87)

	Here is part 1 of 2 of Steve Drew's version of Matt Dillon's
    shell for Aztec C.  This shell archive contains all the
    documentation as well as some of the sources.  Binaries available
    over in comp.binaries.amiga.
	-Craig Norborg
	comp.sources.amiga moderator


#	This is a shell archive.
#	Remove everything above and includeing the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# Xshar: Extended Shell Archiver.
# This is part  1 out of  2.
#	Run the following text with /bin/sh to create:
#	globals.c
#	main.c
#	makefile
#	readme.206m
#	set.c
#	shell.doc
#	sort.c
#	sub.c
# This archive created: Mon Jun  8 12:26:55 1987
# By: Craig Norborg (Purdue University Computing Center)
cat << \SHAR_EOF > globals.c

/*
 * GLOBALS.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87
 *
 *    Most global variables.
 *
 */


#include "shell.h"

struct HIST *H_head, *H_tail;			/* HISTORY lists      */

struct PERROR Perror[] = {			/* error code->string */
   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",
   507,	 "destination not a directory",
   508,	 "cannot mv a filesystem",
     0,	 NULL
};


char  *av[MAXAV];	      /* Internal argument list			*/
long  Src_base[MAXSRC];	      /* file pointers for source files		*/
long  Src_pos[MAXSRC];	      /* seek position storage for same		*/
char  If_base[MAXIF];	      /* If/Else stack for conditionals		*/
int   H_len, H_tail_base;     /* History associated stuff		*/
int   H_stack;		      /* AddHistory disable stack		*/
int   E_stack;		      /* Exception disable stack		*/
int   Src_stack, If_stack;    /* Stack Indexes				*/
int   ac;		      /* Internal argc				*/
int   debug;		      /* Debug mode				*/
int   disable;		      /* Disable com. execution (conditionals)	*/
int   Verbose;		      /* Verbose mode for source files		*/
int   Lastresult;	      /* Last return code			*/
int   Exec_abortline;	      /* flag to abort rest of line		*/
int   Exec_ignoreresult;      /* flag to ignore result			*/
int   Quit;		      /* Quit flag				*/
long  Cout, Cin;	      /* Current input and output file handles	*/
long  Cout_append;	      /* append flag for Cout			*/
long  Uniq;		      /* unique value				*/
char  *Cin_name, *Cout_name;  /* redirection input/output name or NULL	*/
char  *Pipe1, *Pipe2;	      /* the two pipe temp. files		*/
struct Process *Myprocess;
int   S_histlen = 20;	      /* Max # history entries			*/
SHAR_EOF
cat << \SHAR_EOF > main.c

/*
 * MAIN.C
 *
 * Matthew Dillon, 24 Feb 1986
 * (c)1986 Matthew Dillon     9 October 1986
 *
 * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87
 *
 */

#include "shell.h"


int aux; /* for use with aux: driver */

char Inline[256];
#ifndef AZTEC_C
char stdout_buff[STDBUF];
#endif

main(argc, argv)
register char *argv[];
{
#ifdef AZTEC_C
   char *rawgets();
#endif
   char *prompt;
   register int i;
   extern int Enable_Abort;
   init_vars();
   init();
   seterr();
   do_pwd(NULL); /* set $_cwd */

   Enable_Abort = 0;

   for (i = 1; i < argc; ++i) {
      if (argv[i][0] == '-' && argv[i][1] == 'c') {
	    Inline[0] = ' ';
	    Inline[1] = '\000';
	    while (++i < argc) {
		strcat(Inline,argv[i]);
		strcat(Inline," ");
		}
	    exec_command(Inline);
	    main_exit(0);
	    }
      if (argv[i][0] == '-' && argv[i][1] == 'a') {
	  aux = 1;
	  continue;
      }
      strcpy (Inline, "source ");
      strcat (Inline, argv[i]);
      av[1] = argv[i];
      do_source (Inline);
   }

   for (;;) {
      if ((prompt = get_var (LEVEL_SET, V_PROMPT)) == NULL)
	 prompt = "$ ";
      if (breakcheck()) {
	 while (WaitForChar(Input(), 100L) || stdin->_bp < stdin->_bend)
	    gets(Inline);
      }
#ifdef AZTEC_C
      if (Quit || !rawgets(Inline, prompt))
#else
      printf("%s",prompt);
      if (Quit || !gets(Inline))
#endif
	 main_exit(0);
      breakreset();
      if (*Inline)
	 exec_command(Inline);
   }
}

init_vars()
{
   if (IsInteractive(Input()))
      set_var (LEVEL_SET, V_PROMPT, "$ ");
   else
      set_var (LEVEL_SET, V_PROMPT, "");
   set_var (LEVEL_SET, V_HIST,	 "20");
   set_var (LEVEL_SET, V_LASTERR, "0");
   set_var (LEVEL_SET, V_PATH, "ram:,ram:c/,c:,df1:c/,df0:c/");
   set_var (LEVEL_SET, "_insert", "1");
}

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

   stdin->_flags  |= 0x80;         /* make sure we're set as a tty */
   stdout->_flags |= 0x80;	   /* incase of redirection in .login */

#ifdef AZTEC_C
   Close(_devtab[2].fd);
   _devtab[2].mode |= O_STDIO;
   _devtab[2].fd = _devtab[1].fd;  /* set stderr to Output() otherwise */
				   /* don't work with aux driver       */
#else
   stdout->_buff = stdout_buff;
   stdout->_buflen = STDBUF;
#endif

   Myprocess = (struct Process *)FindTask(0L);
   Uniq	 = (long)Myprocess;
   Pipe1 = pipe1;
   Pipe2 = pipe2;
   sprintf (pipe1, "ram:pipe1_%ld", Uniq);
   sprintf (pipe2, "ram:pipe2_%ld", Uniq);
}


main_exit(n)
{
   exit (n);
}

breakcheck()
{
   return (int)(SetSignal(0L,0L) & SIGBREAKF_CTRL_C);
}

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


/* this routine causes manx to use this Chk_Abort() rather than it's own */
/* otherwise it resets our ^C when doing any I/O (even when Enable_Abort */
/* is zero).  Since we want to check for our own ^C's			 */

Chk_Abort()
{
return(0);
}

_wb_parse()
{
}
SHAR_EOF
cat << \SHAR_EOF > makefile
######################################################################
#
# Makefile to build Shell 2.06M
# by Steve Drew 30-May-87
#
######################################################################

OBJS	= run.o main.o comm1.o comm2.o execom.o set.o sub.o \
		globals.o rawconsole.o sort.o

INCL	= shell.h

Shell	: $(OBJS)
	ln  +q -m -o Shell $(OBJS) -lc

rawconsole.o : rawconsole.c $(INCL)
	cc    +IShell.syms rawconsole.c

run.o   : run.c $(INCL)
	cc    +HShell.syms run.c

main.o  : main.c $(INCL)
	cc    +IShell.syms main.c

comm1.o	: comm1.c $(INCL)
	cc    +IShell.syms comm1.c

comm2.o	: comm2.c $(INCL)
	cc    +IShell.syms comm2.c

set.o	: set.c $(INCL)
	cc    +IShell.syms set.c

sub.o	: sub.c $(INCL)
	cc    +IShell.syms sub.c

globals.o : globals.c $(INCL)
	cc    +IShell.syms globals.c

execom.o : execom.c $(INCL)
	cc    +IShell.syms execom.c
SHAR_EOF
cat << \SHAR_EOF > readme.206m


	VERSION RELEASES:  (Manx Versions)
	----------------

	2.06M   30-May-87 Steve Drew  :Compiled with manx 3.4a, bugs fixes,
				      :many new features.
	2.05M   10-Jan-87 Steve Drew  :Few bugs fixed, Matt's new 2.04
				      :features implemented.
	2.04M	09-Dec-86 Steve Drew  :Few bugs fixed, Commandline Editing
				      :& Function key support added.
				      :Changed my version to (M)anx.
	2.02A	20-oct-86 Steve Drew  :Implemented Matt's new features also
				      :now 16 bit compilable.
				      :(Same functionality as Matt's 2.03)
	2.01A	27-sep-86 Steve Drew  :Major conversion from Lattice > MANX
				      :and added more commands/features.

Please send all bug reports/comments to:

Steve Drew at (Digital Equipment Corp)

	ENET:    CGFSV1::DREW
	ARPA:    drew%cfgsv1.dec.com@decwrl.dec.com
	USENET:  {decvax|decwrl}!cgfsv1.dec.com!drew    

or
	52 Castledale Cres N.E.
	Calgary, Alberta
	Canada

Version 2.06M notes: (new features)
-----------------------------------

	- Minor changes for 3.4a of manx. Slightly smaller executable.
          (That of was before the new features).
	- Bug fixes:
	    o New fexec code from 3.4a fixes many problems like some lattice
	      programs causing task helds, when invoked under shell.
            o Limit of 1 line of type a head fixed.
            o Added check in mkdir to see if file already exists.
            o If levels too deep protection added.(prevents potential crash)

	- Better Pathing:
          For all external commands including any 'run' from shell we
          first search $_path variable then search amigados path.
	- Added -a startup switch, turns of command line editing code 
	  for use with AUX or conman handler.
	- Added setfiledate on copy, but maybe disabled via -d switch.
  	- Also -u switch on copy: if dest file is same name and not older
  	  then don't copy.
	- copy command will now create the destination directory if it does
          not exist when specified as 'copy [-r] dir dir'. If you specify
          copy file file file dir, then 'dir' must already exist.

	- Added recursive wild card support via '.../' specifier eg.
	  dir df0:.../*.c will go down all levels of df0: matching on
          any .c file. or echo .../* will expand to all files down all
	  trees from our current level.

	- Exclude pattern matching support, in a limited way , only one
          exclude pattern per cmd line and for files not dirs.
          eg.  
           dir !*.info will 	  exclude all *.info files in current dir.
           dir df0:.../!*.info    full directory tree of df0: but exclude
                                  any ugly .info files.
          dir !*.o !*.c (will result in ALL files matching since what
          doesnt match the !*.o will match the !*.c)

	- Directories always sorted. Sorted directories before were slow
          since files were examined twice once by expand() and then by do_dir.
          Now everything is done by expand() and do_dir() only formats the 
          printed output. -e switch removed since expand does that now.
          eg.
           dir df0:.../           does a full (All) sorted directory

	- Passing an argument that contains a space to an external command
	  is now implement the same way as internal commands, also any
	  expansion of a file name that contains a space will be enclosed
	  in quotes before being pass to an external command.
              the following will now work correctly:
		  $ rm "foo bar"        this worked before
		  $ DELETE "foo bar"    needed "\"foo bar"\" before.
		  $ "my prog"	        would not work before	
		  $ myprog *		would not work with spaced files before

Version 2.05M notes: (new features)
-----------------------------------

	- Shell search path now used on 'run' command as well.
	- New -e, exclude files option on dir command. see shell.doc.
	- Command line editing new key:  ^K - delete to end of line.
        - New variable _insert set to 0 makes default of no insert mode
	  for commandline editing default is 1. (insert mode on).
	- New 'copy' command from Matt's 2.04 'cp' logs files and directorys
	  as they are created and ^C detection. See doc for -r option.
	- Few bugs fixed.

   NEW FEATURES IN 2.04: (from Matt implemented in 2.05M)
	
        - RM command now has '-r' option.
        - \command forces the command parser NOT to look at aliases.  Thus,
          you can alias something like 'cd' and have the alias contain a 'cd'
          which references the internal cd:
           alias cd "stuff...;\\cd $x"
        - _histnum variable .. current history #
        - expanded filenames are sorted.
	   eg. Dir *    will output sorted directory.
 
Version 2.04M notes: (new features)
-----------------------------------
	
	- This version runs UNDER WORKBENCH 1.2 ONLY.
	- COMMAND LINE EDITING
	- Using Function keys.
	- New variable _width shows the number of collums in the window.
	  and will change automatically if the user resizes the window.
	- option -c when starting will invoke the rest of command line
	  and exit. (Thanks Dave.) Usefull to do stuff in the background.
	  e.g. run shell -c copy c:foo ram:;echo done.
	- pwd gets run as part of cd so as to set _cwd to full path name.

 
Version 2.02A notes: 
--------------------
	- For new features of 2.03 see Matt's instruction.txt appended below.
	- All Matt's new feature for 2.03 converted to manx. And uses c.lib.
	- Redirection appears to work fine. Even on bcpl programs.
	  Let me know if you find otherwise.
	- new varible	_path	for setting search path for external
	  cmds. Shell does not use the 1.2 PATH feature but you may
	  specify a search path by setting this symbol.
		e.g.
			$ set _path "ram:,c:,df0:c/,df1:c/"

	- Auto requesters are turned off during searching for cmds
	  except when an explicit path is specified eg. df0:c/sort.
	- Command list is sorted so that help displays readable output.
	- A few bugs fixed
	- Changed all i/o routines that used MY.LIB written by Matt to
	  use standard i/o or Amiga routines, since Manx is so efficeint
	  with standard i/o routines compiling all those library functions
	  did'nt gain anything as it does with Lattice.
	- Dir command rewritten to allow options:
		-s short mutil(4) collum display of files
		-d directorys only
		-f files only
	- Wildcarding now matches upper or lower case.	
	- Command will no longer abort if one of the arguments which
	  has wild card does not expand.
	- run program >redir will work properly as
	  long as you run command is called 'run'. With the lattice 
	  version the command got parsed like run >redir program, so
   	  all you got in you redir file was [CLI n].
	- On startup you current directory is determined and set.
	- Added %full and volume name to devinfo.
	- ps command added
	  

   NEW FEATURES IN 2.03.  Thanks to Steve Drew who suggested a '_path'
   variable.  The other difference with this version is that BCPL
   output redirection works properly.  Additionaly, alias loops are
   detected (this is what is known as a hack).
 
   NEW FEATURES IN 2.02.  I would like to thank Dave Wecker and Steve Drew
   for their numerous comments on the previous version of my shell.
 
   -Return code and error handling
      (A) retrieve return code
      (B) Manual or Automatic error handling.
   -Control C-F now passed to external commands.
   -can execute shell scripts as if they were normal commands (w/ arguments)
    (see down below)
   -BCPL programs which change the CLI state (CD/PATH...) now work with
    the shell.  However, the CLI PATH is not used currently.
   -MV command embellished... can now specify multiple files with a
    directory as the destination.
   -CD re-written, new variable $_cwd. startup directory figured out.
   -Comment character '#'
   -EXIT as an alternate to QUIT
 
   Additional Commands:
      abortline
      forever
 
   Additional SET variables (see documentation below)
      _cwd           current directory (see CD below)
      _maxerr        worst return value to date
      _lasterr       return value from last command (internal or external)
      _except        contains error level AND exception code. "nnn;command"
                     (see ABORTLINE, and start of section E)
 
      _passed        contains passed command line to source files
      _path          contains search path (example:  "c:,df1:c/,df0:c/"
 

SHAR_EOF
cat << \SHAR_EOF > set.c

/*
 * SET.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87
 *
 */

#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 = (unsigned char *)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 (0);
}

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

   if (ac == 1) {
      while (base) {
	 if (CHECKBREAK())
	     return(0);
	 printf ("%-10s ", base->name);
	 puts (base->text);
	 base = base->next;
      }
      return (0);
   }
   if (ac == 2) {
      str = get_var (level, av[1]);
      if (str) {
	 printf ("%-10s ", av[1]);
	 puts(str);
      } else if (level == LEVEL_SET) { /* only create var if set command */
	 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 (0);
}
SHAR_EOF
cat << \SHAR_EOF > shell.doc
		INSTRUCTIONS FOR SHELL V2.06M    20-Jan-87
		-----------------------------

SHELL V2.04. (C)Copyright 1986, Matthew Dillon, All Rights Reserved.
You may distribute this program for non-profit only.

		Shell V2.06M by Steve Drew.
		--------------------------
--------------------------------------------------------------------------
Note:
    These Instructions are my specific 2.06M Instructions and Matt's 2.04
    merged together. 
    A preceding | indicates that funtionality has been changed/enhanced,
    a preceding * indicates that this is functionality or a command
    that has been added in my manx version.

    for version releases see readme file. 
---------------------------------------------------------------------------

      (A)   Compiling
      (B)   Overview
      (C)   Quicky tech notes on implimentation.
 
      (D)   Command pre-processor
      (E)   Command Line Editing
      (F)   Function Keys
      (G)   Command-list
      (H)   special SET variables
 
      (I)   example .login file.
 

 
(A) COMPILING:
 
|   makefile supplied.
|
|   Your manx should be patched for 1.2 (c.lib) otherwise fexec wont work
|   and you'll just get "command not found" for all external commands.


(B) OVERVIEW:
 
   OVERVIEW of the major features:
 
   -simple history
   -redirection
   -piping
   -command search path 
   -aliases
   -variables & variable handling (embedded variables)
   -file name expansion via '?' and '*'
   -conditionals
   -source files  (w/ gotos and labels)
   -many built in commands to speed things up

   PROBLEMS
 
   -Append '>>' does NOT work with BCPL programs.  It does work with all
    internal and non-bcpl programs.
 
   -This version runs UNDER WORKBENCH 1.2 ONLY.
    
 
(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.
 
   The file names used are completely unique, even with multiple shell
   running simultaniously.
 
   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
 
   The BCPL 'RUN' command should not be redirected.. .strange things
   happen.
 
   NO BCPL program should be output-append redirected (>>).
 
 
(D)   Command pre-processor
 
   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). Spaces delimit 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 '\\'.
 
            also used to overide alias searching for commands.
 
   >file    specify output redirection.  All output from the command is
            placed in the specified file.
 
   >>file   specify append redirection (Does not work with BCPL programs).
 
   <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.
 
   #        Enter comment.  The rest of the line is discarded (note: \#
            will, of course, overide the comment character's special
            meaning)
 

(E) Command Line Editing

*  - Command line can be upto 255 chars.
*  - Inserts and deletes are handled correctly over
*    multiple screen lines. The program will keep track of
*    the line width should the window get resized.
*
*	KEY DEFINITIONS:
*   	  	Up Arrow    Recal previous commands
*	  	Down Arrow  Recal commands
*		Left Arrow  Move cursor about command line.
*		Right Arrow  "     "      "      "      "
*		^A	    Toggle insert/overtype mode.
*		^D	    EOF
*		^E	    Put cursor at end of text.
*		^K	    Delete to end of line.
*		^R	    Retype current line.
*		^U	    Erase entire line.
*		^X	    Erase entire line.
*		^Z	    Put cursor at start of text.
*		f1 - f10    Execute command if variable exists.
*		F1 - F10    More commands (Shifted f keys).
*		Help 	    invokes help command

(F) Function keys.
		
*  - Just set the variable f1-f10 or F1-F10 (shifted) to
*    the desired string.
*
*	  eg. 
*  	     $ set f1 "dir -s df0:"			

 
(G)  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.
 
   AUTOMATIC SOURCING may be accomplished by naming shell scripts with a
   .sh suffix.  Thus, if you say 'stuff' and the file 'stuff.sh' exists in
   your current or C: directory, it will be SOURCED with any arguments you
   have placed in the $_passed variable.
 
   EXCEPTION_PROCESSING:
 
      if no _except variable exists, any command which fails causes the
      rest of the line to abort as if an ABORTLINE had been executed.  If
      the _except variable exists, it is of the form:
 
      "nnn;commands..."
 
      where nnn is some value representing the minimum return code required
      to cause an error.  Whenever a command returns a code which i*
      larger or equal to nnn, the commands in _except are executed before
      anything.  WHEN _except EXISTS, THE COMMAND LINE DOES NOT ABORT
      AUTOMATICALLY.  Thus, if you want the current line being executed
      to be aborted, the last command in _except should be an "abortline".
 
      exception handling is disabled while in the exception handling routine
      (thus you can't get into any infinite loops this way).
 
      Thus if _except = ";", return codes are completely ignored.
 
      example:
 
      set _except "20;abortline"
 
 
   ABORTLINE
 
      or just 'abort'.  Causes the rest of the line to be aborted. Used in
      conjunction with exception handling.
 
      % echo a;abort;echo b
      a
 
 
   HELP
 
|     simply displays all the available commands.  The commands are
|     displayed in search-order.  That is, if you give a partial name
|     the first command that matches that name in this list is the one
|     executed.  Generally, you should specify enough of a command so that
|     it is completely unique.
 
   QUIT
   EXIT
   RETURN [n]
 
      quit my SHELL (awww!).  End, El-Zappo, Kapow. Done, Finis.  If you
      use RETURN and are on the top source level, the shell exits with the
      optional return code.  (see RETURN below)
 
 
   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.
 
      see the section on special _ variables down below
 
 
   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 [arguments]
 
      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).  You can pass SOURCE files arguments
      by specifying arguments after the file name.  Arguments are passed
      via the _passed variable (as a single string).
 
      Automatic 'sourcing' is accomplished by placing a .sh extension on
      the file and executing it as you would a C program:
 
      --------- file hello.sh ---------
      foreach i ( $_passed ) "echo yo $i"
      ---------------------------------
      % hello a b c
      yo a
      yo b
      yo c
 
 
   MV from to
   MV from from from ... from todir
 
      Allows you to rename a file or move it around within a disk.  Allows
      you to move 1 or more files into a single directory.
 
 
   CD
   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).
 
      CD without any arguments displays the path of the directory you
      are currently in.
 
 
   PWD
      rebuild _cwd by backtracing from your current directory.
 
 
   RM [-r] file file file...
 
      DeleteFile().  Remove the specified files.  Remove always returns
      errorcode 0.  You can remove empty directories.  The '-r' option
      will remove non-empty directories by recursively removing all sub
      directories.
*     If  you specify any wildcard deletes the files will be listed as
*     they are deleted. This can be suppressed by redirecting to nil: 
*	eg.  rm df0:.../*.o   delete all .o files from every directory.
 
|  COPY [-u][-d] file file
|  COPY [-u][-d] file1 file2...fileN dir
|  COPY [-r][-u][-d] dir1 dir2...dirN dir
 
      copy files or directories.  when copying directories, the "-r" option
      must be specified to copy subdirectories as well.  Otherwise, only
      top level files in the source directory are copied.

*     All files will be displayed as they are copied and directory's displayed
*     as they are created. This output can be suppessed by redirecting to nil:
*	eg. copy -r >nil: df0: df1:
*     copy will abort after current file on Control-C.

|     copy by default sets the date of the destination file to that of
|     the source file. To overide this feature use the -d switch.

|     Another useful option is the -u (update) mode were copy will not
|     copy any files which exists already in the destination directory
|     if the destination file is newer or equal to the source file.
|     This is usefull when developing code say in ram: eg. 'copy *.c ram:'
|     when done you can copy -u ram: df1: and only those modules you have 
|     modified will be copied back.

|     copy command will now create the destination directory if it does
|     not exist when specified as 'copy [-r] dir dir'. If you specify
|     copy file file file dir, then 'dir' must already exist.
 

*  RUN file
*
*     Finds the specified file via $_path variable, or 2nd via AmigaDos
*     PATH setting, then calls AmigaDos Run command with the explicit
*     path/command. This command is really only implemented to allow
*     consistancy in first checking the shell pathing before amigados
*     pathing. Since shell allows for device names in _path it does'nt
*     care when you swap disks; if you had "df0:c/" in _path then whatever
*     disk you have in df0: will be checked. AmigaDos PATH always resolves
*     to a Lock that points only to a particular disk label:directory.


   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 for CHIP and FAST.
 
 
   CAT [file file....]
 
      Type the specified files onto the screen.  If no file is specified,
      STDIN in used.  CAT is meant to output text files only.
 
 
|  DIR [-sdf] [path path ... ]
 
|	- default output show's date, protection, block size, byte size.
*	- Dir command rewritten to allow options:
*		-s short mutil(4) collum display of files
*			(directory files are in italics).
*		-d directorys only
*		-f files only
*
*	  eg. dir -s *.info       (short directory of all .info files.)
				
|	  the directory command will also not expand files that are
|	  directories if as result of a wildcard expansion. eg:
|	  'dir df0:*'  and 'dir df0:' will give same results
|	  expect previously if df0:* was specified all subdirectories
|	  of df0: were expanded also.
|	  (to list one level of subdirectories: 'dir df0:*/*')

|	  Directories always sorted.

|	  Wild card expansions:
|		?	match any single character
|		*	match any string
|		.../	recursive search down ALL sub directories
|
|          dir df0:.../           does a full (All) sorted directory
|	   dir .../*.c		  start at current dir and go down looking
|			          for all .c files.
|
| 
|	  Exclude pattern matching specifier: !
|          dir !*.info will 	  exclude all *.info files in current dir.
|          dir df0:.../!*.info    full directory tree of df0: but exclude
|                                 any ugly .info files.
|          dir !*.o !*.c (will result in ALL files matching since what
|          doesnt match the !*.o will match the !*.c)


   DEVINFO [device: device:... ]
 
      Display Device statistics for the current device (CD base), or
      specified devices.
|     Gives block used/free, % used, errs and volume name.

 
   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
 
      Foreach is especially useful when interpreting passed arguments in
      an alias or source file.
 
      NOTE: a GOTO inside will have indeterminate results.
 
 
   FOREVER command
   FOREVER "command;command;command..."
 
      The specified commands are executed over and over again forever.
 
      -Execution stops if you hit ^C
      -If the commands return with an error code.
 
      NOTE: a GOTO inside will have indeterminate results.
 
 
   RETURN [value]
 
      return from a source file.  The rest of the source file is
      discarded.  If given, the value becomes the return value for the
      SOURCE command.  If you are on the top level, this value is returned
      as the exit code for the shell.
 
 
   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.
 
 
   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.
 
*  PS
 
*    Gives the following info:
*	  
*	  Proc Command Name         CLI Type    Pri.  Address  Directory
*	   1   SHELL                Initial CLI   0      97b0  Stuff:shell
*	   2   sys:c/clockmem       Background  -10    2101a8  Workdisk:
*	   3   c:emacs              Background    0    212f58  Stuff:shell
*	   4   sys:c/VT100          Background    0    227328  Workdisk:
*	
*	   Address is the addres of the task, directory is the process
*	   currently set directory.
 
 
(H) SPECIAL SET VARIABLES
 
|   _prompt
|        This variable is set to the command you wish executed that will
|        create your prompt. Under manx version set this to the string
|	 for your prompt not a command to create the prompt. (Restriction
|	 due to commandline editing support.
 
   _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.  must be set to some value
 
   _verbose
         Verbose mode (for source files).  display commands as they are
         executed.
 
   _maxerr
         The worst (highest) return value to date.  To use this, you usually
         set it to '0', then do some set of commands, then check it.  
 
   _lasterr
         Return code of last command executed.  This includes internal
         commands as well as external comands, so to use this variables
         you must check it IMMEDIATELY after the command in question.
 
   _cwd
         Holds a string representing the current directory we are in from
         root.  The SHELL can get confused as to its current directory if
         some external program changes the directory.  Use PWD to rebuild
         the _cwd variable in these cases.
 
   _passed
         This variable contains the passed arguments when you SOURCE a file
         or execute a .sh file.  For instance:
 
         test a b c d
 
         -------- file test.sh ----------
         echo $_passed
         foreach i ( $_passed ) "echo YO $i"
         --------------------------------
 
 
   _path
         This variable contains the search path when the shell is looking
         for external commands.  The format is:  DIR,DIR,DIR  Each DIR must
         have a trailing ':' or '/'.  The current directory is always
         searched first.  The entire path will be searched first for the
         <command>, then for <command>.sh (automatic shell script sourcing).
 
*        The default _path is set to  "c:,df1:c/,df0:c/,ram:,ram:c/"
*	 When using 'run' command Shell will now use it's own search
*	 path first to find the command to run. If it fails to find
*	 the command (but the Run command was found) it executes the
*	 command line anyway to let amigaDos search path take over.

*  _insert
*        Set's the default for insert/overtype mode for command line
*	 editing. ^A toggles between, but after <RET> the default is 
*         set back as indicated by this variable. By default _insert is 1
*        indicating insert mode on setting to zero will make overtype
*	 the default.

*  _width
*	 Indicates the console window width, 77 if unset. 
* 	 Will change automatically if the user resizes the window.
	
 
(I) 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:
 
------------------------------------------------------------------------
.LOGIN
------------------------------------------------------------------------

# -Steve's .login file- #

echo -n    "Enter Date [DD-MMM-YY HH:MM] ";input new; DATE $new

# ------My search path ------- #

set   _path  ram:c/,ram:,c:,df0:c/,df1:c/,sys:system/,sys:utilities

# -------Function keys-------- #

set   f1     dir df0:
set   f2     dir df1:
set   F1     dir -s df0:
set   F2     dir -s df1:
set   f3     info
set   f4     ps

# ---------Quickies---------- #

# -query delete- #
#another favorite eg    qd *.c
alias qd "%q foreach i ( $q ) \"echo -n Delete [n] ;input a;if $a = y;del $i;endif\""

alias delete rm
alias rename mv
alias makedir mkdir
alias type  cat
alias print "%q copy $q prt:"
alias info  "devinfo df0: df1: ram:"
alias tosys "assign c: SYS:c"
alias toram "assign c: RAM:c;"
alias tomanx "assign c: MANX:c; manxinit"
alias d     "dir -s"
alias clr   "echo -n ^l"
alias wb    "loadwb"
alias pref  "sys:preferences"
alias cal   "run sys:utilities/calculator"

# ------Applications---------- #

alias em    "run emacs"
alias vt    "run sys:c/VT100"

# --------Finish Up----------- #

ver ;echo -n "Shell loaded on ";date


------------------------------------------------------------------------
MANXINIT.SH
------------------------------------------------------------------------
SET INCLUDE=AC:include CCTEMP=ram:
makedir ram:lib;Set CLIB=RAM:lib/;copy AC:lib/$libfile ram:lib"
alias	cleanup	"del >NIL: ram:lib/* ram:lib"

#run make in background at lower priority:
alias	make	"%q run ChangeTaskPri -5 +^J^J MAKE $q"


SHAR_EOF
cat << \SHAR_EOF > sort.c

/*
 * sort.c
 *
 *
 * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87
 *
 * sort has been modified via addition of slashcmp which provides correct
 * sorting of file names in tree/alphabetical order. This was needed
 * due to the recursive wild carding support I added in expand(). /Steve
 */

QuickSort(av, n)
char *av[];
int n;
{
   int b;

   if (n > 0) {
      b = QSplit(av, n);
      QuickSort(av, b);
      QuickSort(av+b+1, n - b - 1);
   }
}


/*
 * QSplit called as a second routine so I don't waste stack on QuickSort's
 * recursivness.
 */

QSplit(av, n)
char *av[];
int n;
{
   int i, b;
   char *element, *scr;

   element = av[0];
   for (b = 0, i = 1; i < n; ++i) {
      if (slashcmp(av[i],element) < 0) {
	 ++b;
	 scr = av[i]; av[i] = av[b]; av[b] = scr;
      }
   }
   scr = av[0]; av[0] = av[b]; av[b] = scr;
   return (b);
}

slashcmp(a,b)
char *a,*b;
{
   char *ap,*bp;
   int b_slash=0,a_slash=0,c;
   char *index();

   if (ap = index(a,'/')) ++a_slash;
   if (bp = index(b,'/')) ++b_slash;

   if (a_slash && b_slash) { /* both contain slashes */
       *bp = *ap = '\0';
       c = strcmp(a,b); /* just compare before slash */
       *bp = *ap = '/';
       if (c == 0) c = slashcmp(ap+1,bp+1); /* recursive */
       return(c);
   }
   if (!a_slash && !b_slash)  /* neither have slashes so just cmp */
       return(strcmp(a,b));
   return(a_slash - b_slash);
}

SHAR_EOF
cat << \SHAR_EOF > sub.c

/*
 * SUB.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87
 *
 */

#include "shell.h"

#define HM_STR 0	      /* various HISTORY retrieval modes */
#define HM_REL 1
#define HM_ABS 2

/* extern struct FileLock *Clock; */
seterr()
{
   char buf[32];
   int stat;

   sprintf(buf, "%d", Lastresult);
   set_var(LEVEL_SET, V_LASTERR, buf);
   stat = atoi(get_var(LEVEL_SET, V_STAT));
   if (stat < Lastresult)
      stat = Lastresult;
   sprintf(buf, "%d", stat);
   set_var(LEVEL_SET, V_STAT, buf);
}


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)   --frees without actually freeing, so the data is still good
 *		 immediately after the free.
 */


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;

   if (H_head != NULL && strcmp(H_head->line, str) == 0)
       return(0);
   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 && *hist->line != '!') {
	    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) {
      fprintf(stderr,"%s\n",result);
      return(result);
   }
   printf("History failed\n");
   return ("");
}

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;
{
   int ierr = (long)IoErr();
   ierror(str, ierr);
}

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

   if (err) {
      for (; per->errstr; ++per) {
	 if (per->errnum == err) {
	    fprintf (stderr, "%s%s%s\n",
		  per->errstr,
		  (str) ? ": " : "",
		  (str) ? str : "");
	    return ((short)err);
	 }
      }
      fprintf (stderr, "Unknown DOS error %ld %s\n", err, (str) ? str : "");
   }
   return ((short)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;

   *stat = 0;
   dp = (struct DPTR *)malloc(sizeof(struct DPTR));
   if (*name == '\0')
      dp->lock = (struct FileLock *)DupLock ((struct FileLock *)Myprocess->pr_CurrentDir);
   else
      dp->lock = (struct FileLock *)Lock (name, ACCESS_READ);
   if (dp->lock == NULL) {
      free (dp);
      return (NULL);
   }
   dp->fib = (struct FileInfoBlock *)
	 AllocMem((long)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,(long)sizeof(*dp->fib));
   if (dp->lock)
      UnLock (dp->lock);
   free (dp);
   return (1);
}


isdir(file)
char *file;
{
   register struct DPTR *dp;
   int stat;

   stat = 0;
   if (dp = dopen (file, &stat))
      dclose(dp);
   return (stat == 1);
}


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

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

/*
 * EXPAND(base,pac)
 *    base	     - char * (example: "df0:*.c")
 *    pac	     - int  *  will be set to # of arguments.
 *
 * 22-May-87 SJD.  Heavily modified to allow recursive wild carding and
 *		   simple directory/file lookups. Returns a pointer to
 *		   an array of pointers that contains the full file spec
 *		   eg. 'df0:c/sear*' would result in : 'df0:C/Search'
 *
 *		   Now no longer necessary to Examine the files a second time
 *		   in do_dir since expand will return the full file info
 *		   appended to the file name. Set by formatfile().
 *		   eg. fullfilename'\0'rwed  NNNNNN NNNN  DD-MMM-YY HH:MM:SS
 *
 *		   Caller must call free_expand when done with the array.
 *
 * base		    bname =	  ename =
 * ------	    -------	  -------
 *  "*"		      ""	    "*"
 *  "!*.info"	      ""	    "*.info" (wild_exclude set)
 *  "su*d/*"	      ""	    "*"	     (tail set)
 *  "file.*"	      ""	    "file.*"
 *  "df0:c/*"	      "df0:c"	    "*"
 *  ""		      ""	    "*"
 *  "df0:.../*"	      "df0:"	    "*"	     (recur set)
 *  "df0:sub/.../*"   "df0:sub"	    "*"	     (recur set)
 *
 * ---the above base would be provided by execom.c or do_dir().
 * ---the below base would only be called from do_dir().
 *
 *  "file.c"	      "file.c"	    ""	     if (dp == 0) fail else get file.c
 *  "df0:"	      "df0:"	    "*"
 *  "file/file"	      "file/file"   ""	     (dp == 0) so fail
 *  "df0:.../"	      "df0:"	    "*"	     (recur set)
 *
 */


char **
expand(base, pac)
char *base;
int *pac;
{
   register char *ptr;
   char **eav = (char **)malloc(sizeof(char *) * (2));
   short eleft, eac;
   char *name;
   char *svfile();
   char *bname, *ename, *tail;
   int stat, recur, scr,wild_exclude,bl;
   register struct DPTR *dp;

   *pac = recur = eleft = eac = wild_exclude = 0;

   base = strcpy(malloc(strlen(base)+1), base);
   for (ptr = base; *ptr && *ptr != '?' && *ptr != '*'; ++ptr);

   if (!*ptr)	/* no wild cards */
      --ptr;
   else
      for (; ptr >= base && !(*ptr == '/' || *ptr == ':'); --ptr);

   if (ptr < base) {
      bname = strcpy (malloc(1), "");
   } else {
      scr = ptr[1];
      ptr[1] = '\0';
      if (!strcmp(ptr-3,".../")) {
	 recur = 1;
	 *(ptr-3) = '\0';
      }
      bname = strcpy (malloc(strlen(base)+2), base);
      ptr[1] = scr;
   }
   bl = strlen(bname);
   ename = ++ptr;
   for (; *ptr && *ptr != '/'; ++ptr);
   scr = *ptr;
   *ptr = '\0';
   if (scr) ++ptr;
   tail = ptr;
   if (*ename == '!') wild_exclude = 1;

   if ((dp = dopen (bname, &stat)) == NULL || (stat == 0 && *ename)) {
      free (bname);
      free (base);
      free (eav);
      return (NULL);
   }

   if (!stat) {		       /* eg. 'dir file' */
      char *p,*s;
      for(s = p = bname; *p; ++p) if (*p == '/' || *p == ':') s = p;
      if (s != bname) ++s;
      *s ='\0';
      eav[eac++] = svfile(bname,dp->fib->fib_FileName,dp->fib);
      goto done;
   }
   if (!*ename) ename = "*";	/* eg. dir df0: */
   if (*bname && bname[bl-1] != ':' && bname[bl-1] != '/') { /* dir df0:c */
      bname[bl] = '/';
      bname[++bl] = '\0';
   }
   while ((dnext (dp, &name, &stat)) && !breakcheck()) {
	int match = compare_ok(ename+wild_exclude,name);

      if (wild_exclude) match ^= 1;
      if (match && !(!recur && *tail)) {
	 if (eleft < 2) {
	       char **scrav = (char **)malloc(sizeof(char *) * (eac + 10));
	       movmem (eav, scrav, (eac + 1) << 2);
	       free (eav);
	       eav = scrav;
	       eleft = 10;
	 }
	 eav[eac++] = svfile(bname,name,dp->fib);
	 --eleft;
      }
      if ((*tail && match) || recur) {
	 int alt_ac;
	 char *search, **alt_av, **scrav;
	 struct FileLock *lock;

	 if (!stat)	      /* expect more dirs, but this not a dir */
	    continue;
	 lock = (struct FileLock *)CurrentDir (dp->lock);
	 search = malloc(strlen(ename)+strlen(name)+strlen(tail)+5);
	 strcpy (search, name);
	 strcat (search, "/");
	 if (recur) {
	    strcat(search, ".../");
	    strcat(search, ename);
	 }
	 strcat (search, tail);
	 scrav = alt_av = expand (search, &alt_ac);
	 /* free(search); */
	 CurrentDir (lock);
	 if (scrav) {
	    while (*scrav) {
	       int l;
	       if (eleft < 2) {
		  char **scrav = (char **)malloc(sizeof(char *) * (eac + 10));
		  movmem (eav, scrav, (eac + 1) << 2);
		  free (eav);
		  eav = scrav;
		  eleft = 10;
	       }

	       l = strlen(*scrav);
	       scrav[0][l] = ' ';
	       eav[eac] = malloc(bl+l+40);
	       strcpy(eav[eac], bname);
	       strcat(eav[eac], *scrav);
	       eav[eac][l+bl] = '\0';

	       free (*scrav);
	       ++scrav;
	       --eleft, ++eac;
	    }
	    free (alt_av);
	 }
      }
   }
done:
   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;
{
   register char *w = wild;
   register char *n = name;
   char *back[MAXB][2];
   register char s1, s2;
   int	bi = 0;

   while (*n || *w) {
      switch (*w) {
      case '*':
	 if (bi == MAXB) {
	    printf(stderr,"Too many levels of '*'\n");
	    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:
	 s1 = (*n >= 'A' && *n <= 'Z') ? *n - 'A' + 'a' : *n;
	 s2 = (*w >= 'A' && *w <= 'Z') ? *w - 'A' + 'a' : *w;
	 if (s1 != s2) {
	    if (bi)
	       goto goback;
	    return (0);
	 }
	 break;
      }
      if (*n)  ++n;
      if (*w)  ++w;
   }
   return (1);
}


char *
svfile(s1,s2,fib)
char *s1,*s2;
struct FileInfoBlock *fib;
{
   char *p;
   p = malloc (strlen(s1)+strlen(s2)+40);
   strcpy(p, s1);
   strcat(p, s2);
   formatfile(p,fib);
   return(p);
}

/* will have either of these formats:
 *
 *    fullfilename'\0'rwed    <Dir>	 DD-MMM-YY HH:MM:SS\n'\0'
 *    fullfilename'\0'rwed  NNNNNN NNNN	 DD-MMM-YY HH:MM:SS\n'\0'
 *		      0123456789012345678901234567890123456 7  8
 *
 */
formatfile(str,fib)
char *str;
register struct FileInfoBlock *fib;
{
   char *dates();

   while(*str++);
   *str++ = (fib->fib_Protection & FIBF_READ)	 ? '-' : 'r';
   *str++ = (fib->fib_Protection & FIBF_WRITE)	 ? '-' : 'w';
   *str++ = (fib->fib_Protection & FIBF_EXECUTE) ? '-' : 'e';
   *str++ = (fib->fib_Protection & FIBF_DELETE)	 ? '-' : 'd';

   if (fib->fib_DirEntryType < 0)
      sprintf(str,"  %6ld %4ld  ", (long)fib->fib_Size, (long)fib->fib_NumBlocks);
   else
      strcpy(str,"    <Dir>      ");
   strcat(str,dates(&fib->fib_Date));
}
SHAR_EOF

doc@s.cc.purdue.edu (Craig Norborg) (06/08/87)

	Here is part 2 of 2 of Steve Drew's version of Matt Dillon's
    shell for Aztec C.
	-Craig Norborg
	comp.sources.amiga moderator


#	This is a shell archive.
#	Remove everything above and includeing the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# Xshar: Extended Shell Archiver.
# This is part  2 out of  2.
#	Run the following text with /bin/sh to create:
#	comm1.c
#	comm2.c
#	execom.c
#	rawconsole.c
#	run.c
#	shell.h
# This archive created: Mon Jun  8 12:26:55 1987
# By: Craig Norborg (Purdue University Computing Center)
cat << \SHAR_EOF > comm1.c
/*
 * COMM1.C
 *
 * Matthew Dillon, August 1986
 *
 * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87
 *
 */

#include "shell.h"
typedef struct FileInfoBlock FIB;

#define DIR_SHORT 0x01
#define DIR_FILES 0x02
#define DIR_DIRS  0x04

extern int has_wild;
char cwd[256];

/*
    Parse the options specified in sw[]
    Setting a bit for each one found
*/
get_opt(sw,count)
char *sw;
int *count;
{
   int l,i = 0, opt = 0;
   char *c,*s;

   while((++i < ac) && (av[i][0] == '-')) {
	for (c = av[i]+1; *c ; c++) {
	    for(l = 0,s = sw;*s && *s != *c; ++s) ++l;
	    if (*s) opt |= (1 << l);
	}
   }
   *count = i;
   return(opt);
}

do_sleep()
{
   register int i;

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


do_number()
{
   return (0);
}

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

   if (ac == 1) {
      while (gets(buf)) {
	 if (CHECKBREAK()) break;
	 puts(buf);
	 }
      clearerr(stdin);
      return (0);
   }

   for (i = 1; i < ac; ++i) {
      if ((fi = fopen (av[i], "r")) != 0) {
	   while (fgets(buf,256,fi)) {
	    fputs(buf,stdout);
	    fflush(stdout);
	    if (CHECKBREAK()) {
	       breakreset();
	       break;
	    }
	 }
	 fclose (fi);
      } else {
	 ierror(av[i], 205);
      }
   }
   return (0);
}

do_devinfo()
{
   struct DPTR		*dp;
   struct InfoData	*info;
   int stat,i;
   char *p,*s,*get_pwd(),*index();

   if (ac == 1) {
      ++ac;
      av[1] = "";
   }
   for (i=1; i < ac; ++i) {
      if (!(dp = dopen (av[i], &stat)))
	 continue;
      info = (struct InfoData *)AllocMem((long)sizeof(struct InfoData), MEMF_PUBLIC);
      if (Info (dp->lock, info)) {

	 s = get_pwd(dp->lock);
	 p = index(s,':');
	 *p = '\0';

	 printf ("Unit:%2ld  Errs:%3ld	Used: %-4ld %3ld%% Free: %-4ld	Volume: %s\n",
	     info->id_UnitNumber,
	     info->id_NumSoftErrors,
	     info->id_NumBlocksUsed,
	     (info->id_NumBlocksUsed * 100)/ info->id_NumBlocks,
	     (info->id_NumBlocks - info->id_NumBlocksUsed),
	     s);

      } else {
	     pError (av[i]);
      }
      FreeMem (info,(long) sizeof(*info));
      dclose(dp);
   }
   return(0);
}



/* things shared with display_file */

char  lspec[128];
int   filecount, col;
long  bytes, blocks;

/*
 * the args passed to do_dir will never be expanded
 */
do_dir()
{
   void display_file();
   int i, options;

   col = filecount = 0;
   bytes = blocks = 0L;
   *lspec = '\0';

   options  = get_opt("sfd",&i);

   if (ac == i) {
      ++ac;
      av[i] = "";
   }
   if (!(options & (DIR_FILES | DIR_DIRS)))  options |= (DIR_FILES | DIR_DIRS);

   for (; i < ac; ++i) {
      char **eav;
      int c,eac;
      if (!(eav = expand(av[i], &eac)))
	 continue;
      QuickSort(eav, eac);
      for(c=0;c < eac && !breakcheck();++c) display_file(options,eav[c]);
      free_expand (eav);
      if (CHECKBREAK()) break;
   }
   if (col)  printf("\n");
   if (filecount > 1) {
      blocks += filecount;     /* account for dir blocks */
      printf (" %ld Blocks, %ld Bytes used in %d files\n", blocks, bytes, filecount);
   }
   return (0);
}

void
display_file(options,filestr)
int options;
char *filestr;
{
   long atol();
   int isadir,slen;
   char sc;
   char *c,*s,*fi;
   struct FileLock *lock;
   char *get_pwd();
   char *strcpy();
   
/*     if current dir different from lspec then
       look for ':' or '/' if found lock it and get_pwd.
       else then use cwd.
*/
   for(s = c = filestr; *c; ++c) if (*c == ':' || *c == '/') s = c;
   if (*s == ':') ++s;
   sc = *s;
   *s = '\0';
   c = filestr;
   if (!*c) c = cwd;
   if (strcmp (c, &lspec))  {
      strcpy(lspec, c);
      if (col)	  printf("\n");
      if (lock = (struct FileLock *)Lock(c,SHARED_LOCK)) {
	 printf ("Directory of %s\n", get_pwd(lock));
	 UnLock(lock);
      }
      col = 0;
   }
   *s = sc;
   if (sc == '/') s++;
   slen = strlen(s);
   fi = s + slen + 1;
   isadir = (fi[9] =='D');

   if (!(((options & DIR_FILES) && isadir) ||
	 ((options & DIR_DIRS)	&& !isadir)))
      return;

   if (options & DIR_SHORT) {
      if ((col == 3) && slen >18) {
	 printf("\n");
	 col = 0;
      }
      if (isadir)  {
	 printf ("\033[3m");
      }
      if (slen >18) {
	 printf(" %-37s",s);
	 col += 2;
      }
      else {
	 printf(" %-18s",s);
	 col++;
      }
      if (col > 3) {
	 printf("\n");
	 col = 0;
      }
      if (isadir) printf("\033[0m");
   }
   else		/* print full info */
      printf("   %-24s %s",s ,fi);
   fflush(stdout);
   fi[12] = fi[17] = '\0';
   bytes  += atol(fi+6);
   blocks += atol(fi+13);
   filecount++;
   return;
}

/*
   converts dos date stamp to a time string of form dd-mmm-yy
*/
char *
dates(dss)
struct DateStamp *dss;
{
   register struct tm tm;
   register long time, t;
   register int i;
   static char timestr[20];
   static char months[12][4] = {
	"Jan","Feb","Mar","Apr","May","Jun",
	"Jul","Aug","Sep","Oct","Nov","Dec"
   };
   static char days[12] = {
	31,28,31,30,31,30,31,31,30,31,30,31
   };
   time = dss->ds_Days * 24 * 60 * 60 + dss->ds_Minute * 60 +
				       dss->ds_Tick/TICKS_PER_SECOND;
   tm.tm_sec = time % 60; time /= 60;
   tm.tm_min = time % 60; time /= 60;
   tm.tm_hour= time % 24; time /= 24;
   tm.tm_wday= time %  7;
   tm.tm_year= 78 + (time/(4*365+1)) * 4; time %= 4 * 365 + 1;
   while (time) {
	t = 365;
	if ((tm.tm_year&3) == 0) t++;
	if (time < t) break;
	time -= t;
	tm.tm_year++;
   }
   tm.tm_yday = ++time;
   for (i=0;i<12;i++) {
	t = days[i];
	if (i == 1 && (tm.tm_year&3) == 0) t++;
	if (time <= t) break;
	time -= t;
   }
   tm.tm_mon = i;
   tm.tm_mday = time;

   sprintf(timestr,"%02d-%s-%2d %02d:%02d:%02d\n",tm.tm_mday,
		months[tm.tm_mon],tm.tm_year,
		tm.tm_hour,tm.tm_min,tm.tm_sec);
   return(timestr);

}

date()
{
   struct   DateStamp	dss;
   char *dates();

   DateStamp(&dss);
   printf("%s",dates(&dss));
   return(0);
}

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;
   }
   printf("%s",ptr);
   fflush(stdout);
   if (nl)
      printf("\n");
   return (0);
}

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

   if (Src_stack == MAXSRC) {
      fprintf (stderr,"Too many source levels\n");
      return(-1);
   }
   if ((fi = fopen (av[1], "r")) == 0) {
      ierror(av[1], 205);
      return(-1);
   }
   set_var(LEVEL_SET, V_PASSED, next_word(next_word(str)));
   ++H_stack;
   Src_pos[Src_stack] = 0;
   Src_base[Src_stack] = (long)fi;
   ++Src_stack;
   while (fgets (buf, 256, fi)) {
      buf[strlen(buf)-1] = '\0';
      Src_pos[Src_stack - 1] += 1+strlen(buf);
      if (Verbose)
	 fprintf(stderr,"%s\n",buf);
      exec_command (buf);
      if (CHECKBREAK())
	 break;
   }
   --H_stack;
   --Src_stack;
   unset_level(LEVEL_LABEL + Src_stack);
   unset_var(LEVEL_SET, V_PASSED);
   fclose (fi);
   return (0);
}


/*
 * return ptr to string that contains full cwd spec.
 */
char *
get_pwd(flock)
struct FileLock *flock;
{
   char *ptr;
   char *name;
   int err=0;
   static char pwdstr[256];

   struct FileLock *lock, *newlock;
   FIB *fib;
   int i, len;

   lock = (struct FileLock *)DupLock(flock);
         
   fib = (FIB *)AllocMem((long)sizeof(FIB), MEMF_PUBLIC);
   pwdstr[i = 255] = '\0';

   while (lock) {
      newlock = (struct FileLock *)ParentDir(lock);
      if (!Examine(lock, fib)) ++err;
      name = fib->fib_FileName;
      if (*name == '\0')	    /* HACK TO FIX RAM: DISK BUG */
	 name = "RAM";
      len = strlen(name);
      if (newlock) {
	 if (i == 255) {
	    i -= len;
	    bmov(name, pwdstr + i, len);
	 } else {
	    i -= len + 1;
	    bmov(name, pwdstr + i, len);
	    pwdstr[i+len] = '/';
	 }
      } else {
	 i -= len + 1;
	 bmov(name, pwdstr + i, len);
	 pwdstr[i+len] = ':';
      }
      UnLock(lock);
      lock = newlock;
   }
   FreeMem(fib, (long)sizeof(FIB));
   movmem(pwdstr + i, pwdstr, 256 - i);
   if (err) return(cwd);
   return(pwdstr);
}

/*
 * set process cwd name and $_cwd, if str != NULL also print it.
 */
do_pwd(str)
char *str;
{
   char *ptr;

   if ((struct FileLock *)Myprocess->pr_CurrentDir == 0)
       attempt_cd(":"); /* if we just booted 0 = root lock */
   strcpy(cwd,get_pwd(Myprocess->pr_CurrentDir,1));
   if (str)
      puts(cwd);
   set_var(LEVEL_SET, V_CWD, cwd);
   /* put the current dir name in our CLI task structure */
   ptr = (char *)((ULONG)((struct CommandLineInterface *)
      BADDR(Myprocess->pr_CLI))->cli_SetName << 2);
   ptr[0] = strlen(cwd);
   movmem(cwd,ptr+1,(int)ptr[0]);
   return(0);
}


/*
 * CD
 *
 * CD(str, 0)	    -do CD operation.
 *
 *    standard operation: breakup path by '/'s and process independantly
 *    x:    -reset cwd base
 *    ..    -remove last cwd element
 *    N	    -add N or /N to cwd
 */

do_cd(str)
char *str;
{
   char sc, *ptr;
   int err=0;

   str = next_word(str);
   if (*str == '\0') {
      puts(cwd);
      return(0);
   }
   str[strlen(str)+1] = '\0';	       /* add second \0 on end */
   while (*str) {
      for (ptr = str; *ptr && *ptr != '/' && *ptr != ':'; ++ptr);
      switch (*ptr) {
      case ':':
	 sc = ptr[1];
	 ptr[1] = '\0';
	 err = attempt_cd(str);
	 ptr[1] = sc;
	 break;
      case '\0':
      case '/':
	 *ptr = '\0';
	 if (strcmp(str, "..") == 0 || str == ptr)
	    str = "/";
	 if (*str) err = attempt_cd(str);
	 break;
      }
      if (err) break;
      str = ptr + 1;
   }
   do_pwd(NULL);	 /* set $_cwd */
   return(err);
}

attempt_cd(str)
char *str;
{
   struct FileLock *oldlock, *filelock;

   if (filelock = (struct FileLock *)Lock(str, ACCESS_READ)) {
      if (isdir(str)) {
	 if (oldlock = (struct FileLock *)CurrentDir(filelock))
	    UnLock(oldlock);
	 return (0);
      }
      UnLock(filelock);
      ierror(str, 212);
   } else {
      ierror(str, 205);
   }
   return (-1);
}

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

   for (i = 1; i < ac; ++i) {
      if (lock = (struct FileLock *)Lock(av[i],ACCESS_READ)) {
	 ierror(av[i],203);
	 UnLock (lock);
	 continue;
      }
      if (lock = (struct FileLock *)CreateDir (av[i])) {
	 UnLock (lock);
	 continue;
      }
      pError (av[i]);
   }
   return (0);
}


do_mv()
{
   char dest[256];
   register int i;
   char *str;

   --ac;
   if (isdir(av[ac])) {
      for (i = 1; i < ac; ++i) {
	 str = av[i] + strlen(av[i]) - 1;
	 while (str != av[i] && *str != '/' && *str != ':')
	    --str;
	 if (str != av[i])
	    ++str;
	 if (*str == 0) {
	    ierror(av[i], 508);
	    return (-1);
	 }
	 strcpy(dest, av[ac]);
	 if (dest[strlen(dest)-1] != ':')
	    strcat(dest, "/");
	 strcat(dest, str);
	 if (Rename(av[i], dest) == 0)
	    break;
      }
      if (i == ac)
	 return (1);
   } else {
      i = 1;
      if (ac != 2) {
	 ierror("", 507);
	 return (-1);
      }
      if (Rename (av[1], av[2]))
	 return (0);
   }
   pError (av[i]);
   return (-1);
}

rm_file(file)
char *file;
{
      if (has_wild) printf("  %s...",file);
      fflush(stdout);
      if (!DeleteFile(file))
	 pError (file);
      else
	 if (has_wild) printf("Deleted\n");
}

do_rm()
{
   int i, recur;

   recur = get_opt("r",&i);

   for (; i < ac; ++i) {
      if (CHECKBREAK()) break;
      if (isdir(av[i]) && recur)
	 rmdir(av[i]);
      if (!(recur && av[i][strlen(av[i])-1] == ':'))
	 rm_file(av[i]);
   }
   return (0);
}

rmdir(name)
char *name;
{
   register struct FileLock *lock, *cwd;
   register FIB *fib;
   register char *buf;

   buf = (char *)AllocMem(256L, MEMF_PUBLIC);
   fib = (FIB *)AllocMem((long)sizeof(FIB), MEMF_PUBLIC);

   if (lock = (struct FileLock *)Lock(name, ACCESS_READ)) {
      cwd = (struct FileLock *) CurrentDir(lock);
      if (Examine(lock, fib)) {
	 buf[0] = 0;
	 while (ExNext(lock, fib)) {
	    if (CHECKBREAK()) break;
	    if (isdir(fib->fib_FileName))
	       rmdir(fib->fib_FileName);
	    if (buf[0]) {
	       rm_file(buf);
	    }
	    strcpy(buf, fib->fib_FileName);
	 }
	 if (buf[0] && !CHECKBREAK()) {
	    rm_file(buf);
	 }
      }
      UnLock(CurrentDir(cwd));
   } else {
      pError(name);
   }
   FreeMem(fib, (long)sizeof(FIB));
   FreeMem(buf, 256L);
}



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) {
	 printf ("%3d ", i);
	 puts (hist->line);
      }
      ++i;
      if (CHECKBREAK())
	 break;
   }
   return (0);
}

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

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

   if (ffree)	    {
   printf ("FAST memory: %ld\n", ffree);
   printf ("CHIP memory: %ld\n", cfree);
   }
   printf ("Total  Free: %ld\n", cfree + ffree);
   return(0);
}

/*
 * 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) {
      fprintf (stderr,"')' expected\n");
      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 (0);
}


do_forever(str)
char *str;
{
   int rcode = 0;
   char *ptr = next_word(str);

   ++H_stack;
   for (;;) {
      if (CHECKBREAK()) {
	 rcode = 20;
	 break;
      }
      if (exec_command (ptr) < 0) {
	 str = get_var(LEVEL_SET, V_LASTERR);
	 rcode = (str) ? atoi(str) : 20;
	 break;
      }
   }
   --H_stack;
   return (rcode);
}

SHAR_EOF
cat << \SHAR_EOF > comm2.c

/*
 * COMM2.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87
 *
 */

#include "shell.h"
typedef struct FileInfoBlock FIB;

/* Casting conveniences */
#define BPTR_TO_C(strtag, var)	((struct strtag *)(BADDR( (ULONG) var)))
#define PROC(task)		((struct Process *)task)
#define ROOTNODE		((struct RootNode *)DOSBase->dl_Root)
#define CLI(proc)		(BPTR_TO_C(CommandLineInterface, proc->pr_CLI))

/* Externs */
extern int has_wild;			/* flag set if any arg has a ? or * */
extern struct DosLibrary *DOSBase;	/* dos library base pointer         */

/* globals */
int cp_update;
int cp_date;

do_abortline()
{
   Exec_abortline = 1;
   return (0);
}

do_return()
{
   Exec_abortline = 1;
   if (Src_stack) {
      fseek (Src_base[Src_stack - 1], 0, 2);
      return ((ac < 2) ? 0 : atoi(av[1]));
   } else {
      main_exit ((ac < 2) ? 0 : 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]);
   return (0);
}

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



/*
 * 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;
   if (If_stack >= MAXIF) {
      fprintf(stderr,"If's too deep\n");
      disable = If_stack = 0;
      return(-1);
      }
   return (0);
}

do_label()
{
   char aseek[32];

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

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);
	 fseek (Src_base[Src_stack - 1], pos, 0);
	 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;
      }
   }
   Exec_abortline = 1;
   return (0);	    /* Don't execute rest of this line */
}


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, "%d", atoi(var)+com);
      set_var (LEVEL_SET, av[1], num);
   }
   return (0);
}

do_input()
{
   char in[256];

   if ((gets(in)) != 0)
      set_var (LEVEL_SET, av[1], in);
   return (0);
}

do_ver()
{
   puts (VERSION);
   return (0);
}


do_ps()
{
	/* this code fragment based on ps.c command by Dewi Williams */

	register ULONG	 *tt;		/* References TaskArray		*/
	register int	 count;		/* loop variable		*/
	register UBYTE	 *port;		/* msgport & ptr arith		*/
	register struct Task *task;	/* EXEC descriptor		*/
	char		 strbuf[64];   /* scratch for btocstr()	       */
	char		 *btocstr();	/* BCPL BSTR to ASCIIZ		*/

	tt = (unsigned long *)(BADDR(ROOTNODE->rn_TaskArray));

	printf("Proc Command Name	  CLI Type    Pri.  Address  Directory\n");
	Forbid();		/* need linked list consistency */

	for (count = 1; count <= (int)tt[0] ; count++) {/* or just assume 20?*/
		if (tt[count] == 0) continue;		/* nobody home */

		/* Start by pulling out MsgPort addresses from the TaskArray
		 * area. By making unwarranted assumptions about the layout
		 * of Process and Task structures, we can derive these
		 * descriptors. Every task has an associated process, since
		 * this loop drives off a CLI data area.
		 */

		port = (UBYTE *)tt[count];
		task = (struct Task *)(port - sizeof(struct Task));

		/* Sanity check just in case */
		if (PROC(task)->pr_TaskNum == 0 || PROC(task)->pr_CLI == 0)
			continue;		/* or complain? */

			btocstr(CLI(PROC(task))->cli_CommandName, strbuf);
			printf("%2d   %-21s",count,strbuf);
			strcpy(strbuf,task->tc_Node.ln_Name);
			strbuf[11] = '\0';
			printf("%-11s",strbuf);
			printf(" %3d  %8lx  %s\n",
			   task->tc_Node.ln_Pri,task,
			   btocstr(CLI(PROC(task))->cli_SetName, strbuf));
	}
	Permit();		/* outside critical region */
	return(0);
}


char *
btocstr(b, buf)
ULONG	b;
char	*buf;
{
	register char	*s;

	s = (char *)BADDR(b);	/* Shift & get length-prefixed str */
	movmem(s +1, buf, s[0]);
	buf[s[0]] = '\0';
	return buf;
}


/*
 * CP [-d] [-u] file file
 * CP [-d] [-u] file file file... destdir
 * CP [-r][-u][-d] dir dir dir... destdir
 */

char *errstr;	       /* let's be alittle more informative */
do_copy()
{
   register int recur, ierr;
   register char *destname;
   register char destisdir;
   register FIB *fib;
   int i,opt;

   errstr = "";
   ierr = 0;

   fib = (FIB *)AllocMem((long)sizeof(FIB), MEMF_PUBLIC);

   opt = get_opt("rud",&i);
   recur     = (opt & 0x01);
   cp_update = (opt & 0x02);
   cp_date   = (!(opt & 0x04)); /* the default is keep orignal file date */

   destname = av[ac - 1];

   if (ac < i + 2) {
      ierr = 500;
      goto done;
   }

   destisdir = isdir(destname);
   if (ac > i + 2 && !destisdir) {
      ierr = 507;
      goto done;
   }

   /*
    * copy set:			       reduce to:
    *	 file to file			  file to file
    *	 dir  to file (NOT ALLOWED)
    *	 file to dir			  dir to dir
    *	 dir  to dir			  dir to dir
    *
    */

   for (; i < ac - 1; ++i) {
      short srcisdir = isdir(av[i]);
      if (srcisdir && has_wild && (ac >2)) /* hack to stop dir's from */
	  continue;			   /* getting copied if specified */
					   /* from wild expansion */
      if (CHECKBREAK())
	 break;
      if (srcisdir) {
	 struct FileLock *srcdir, *destdir;
	 if (!destisdir) {
	    if (destdir = (struct FileLock *)Lock(destname, ACCESS_READ)) {
	       UnLock(destdir);
	       ierr = 507;		  /* disallow dir to file */
	       goto done;
	    }
	    if (destdir = (struct FileLock *)CreateDir(destname))
	       UnLock(destdir);
	    destisdir = 1;
	 }
	 if (!(destdir = (struct FileLock *)Lock(destname, ACCESS_READ))) {
	    ierr = 205;
	    errstr = destname;
	    goto done;
	 }
	 if (!(srcdir = (struct FileLock *)Lock(av[i], ACCESS_READ))) {
	    ierr = 205;
	    errstr = av[i];
	    UnLock(destdir);
	    goto done;
	 }
	 ierr = copydir(srcdir, destdir, recur);
	 UnLock(srcdir);
	 UnLock(destdir);
	 if (ierr)
	    break;
      } else {			    /* FILE to DIR,   FILE to FILE   */
	 struct FileLock *destdir, *srcdir, *tmp;
	 char *destfilename;

	 srcdir = (struct FileLock *)(Myprocess->pr_CurrentDir);

	 if ((tmp = (struct FileLock *)Lock(av[i], ACCESS_READ)) == NULL || !Examine(tmp,fib)){
	    if (tmp) UnLock(tmp);
	    ierr = 205;
	    errstr = av[i];
	    goto done;
	 }
	 UnLock(tmp);
	 if (destisdir) {
	    destdir = (struct FileLock *)Lock(destname, ACCESS_READ);
	    destfilename = fib->fib_FileName;
	 } else {
	    destdir = srcdir;
	    destfilename = destname;
	 }
	 printf(" %s..",av[i]);
	 fflush(stdout);
	 ierr = copyfile(av[i], srcdir, destfilename, destdir);
	 if (destisdir)
	    UnLock(destdir);
	 if (ierr)
	    break;
      }
   }
done:
   FreeMem(fib, (long)sizeof(*fib));
   if (ierr) {
      ierror(errstr, ierr);
      return(20);
   }
   return(0);
}


copydir(srcdir, destdir, recur)
register struct FileLock *srcdir, *destdir;
{
   struct FileLock *cwd;
   register FIB *srcfib;
   register struct FileLock *destlock, *srclock;
   int ierr;
   static int level;

   level++;
   ierr = 0;
   srcfib = (FIB *)AllocMem((long)sizeof(FIB), MEMF_PUBLIC);
   if (Examine(srcdir, srcfib)) {
      while (ExNext(srcdir, srcfib)) {
	 if (CHECKBREAK())
	    break;
	 if (srcfib->fib_DirEntryType < 0) {
	    printf("%*s%s..",(level-1) * 6," ",srcfib->fib_FileName);
	    fflush(stdout);
	    ierr = copyfile(srcfib->fib_FileName,srcdir,srcfib->fib_FileName,destdir);
	    if (ierr)
	       break;
	 } else {
	    if (recur) {
	       cwd = (struct FileLock *)CurrentDir(srcdir);
	       if (srclock = (struct FileLock *)Lock(srcfib->fib_FileName, ACCESS_READ)) {
		  CurrentDir(destdir);
		  if (!(destlock = (struct FileLock *)
				Lock(srcfib->fib_FileName))) {
		     destlock = (struct FileLock *)CreateDir(srcfib->fib_FileName);
		     printf("%*s%s (Dir)....[Created]\n",(level-1) * 6,
				" ",srcfib->fib_FileName);

 			/* UnLock and re Lock if newly created dir
 			   for file_date() to work properly.
 			*/   
 		     if (destlock) UnLock(destlock);
 		     destlock = (struct FileLock *)Lock(srcfib->fib_FileName);		     
		  }
		  else
		     printf("%*s%s (Dir)\n",(level-1) * 6," ",srcfib->fib_FileName);
		  if (destlock) {
		     ierr = copydir(srclock, destlock, recur);
		     UnLock(destlock);
		  } else {
		     ierr = (int)((long)IoErr());
		  }
		  UnLock(srclock);
	       } else {
		  ierr = (int)((long)IoErr());
	       }
	       CurrentDir(cwd);
	       if (ierr)
		  break;
	    }
	 }
      }
   } else {
      ierr = (int)((long)IoErr());
   }
   --level;
   FreeMem(srcfib, (long)sizeof(FIB));
   return(ierr);
}


copyfile(srcname, srcdir, destname, destdir)
char *srcname, *destname;
struct FileLock *srcdir, *destdir;
{
   struct FileLock *cwd;
   struct FileHandle *f1, *f2;
   struct DateStamp *ds;
   long i;
   int stat,ierr;
   char *buf;
   struct DPTR		*dp, *dps = NULL;

   buf = (char *)AllocMem(8192L, MEMF_PUBLIC|MEMF_CLEAR);
   if (buf == NULL) {
      ierr = 103;
      goto fail;
   }

   ierr = 0;
   cwd = (struct FileLock *)CurrentDir(srcdir);
   f1 = Open(srcname, MODE_OLDFILE);
   if (f1 == NULL) {
      errstr = srcname;
      ierr = 205;
      goto fail;
   }
   dps = dopen(srcname,&stat);
   ds = &dps->fib->fib_Date;
   CurrentDir(destdir);
   if (cp_update  && (dp = dopen (destname, &stat))) {
	long desttime,srctime;
	struct DateStamp *dd;

	dd = &dp->fib->fib_Date;
	desttime = dd->ds_Days * 24 * 60 * 60 + dd->ds_Minute * 60 +
				       dd->ds_Tick/TICKS_PER_SECOND;
	srctime	 = ds->ds_Days * 24 * 60 * 60 + ds->ds_Minute * 60 +
				       ds->ds_Tick/TICKS_PER_SECOND;

	if (srctime <= desttime &&
	  !strcmp(dps->fib->fib_FileName,dp->fib->fib_FileName)) {
	    dclose(dp);
	    Close(f1);
	    printf("..not newer\n");
	    goto fail;
	}
	dclose(dp);
   }
   f2 = Open(destname, MODE_NEWFILE);
   if (f2 == NULL) {
      Close(f1);
      ierr = (int)((long)IoErr());
      errstr = destname;
      goto fail;
   }
   while (i = Read(f1, buf, 8192L))
      if (Write(f2, buf, i) != i) {
	 ierr = (int)((long)IoErr());
	 break;
      }
   Close(f2);
   Close(f1);
   if (!ierr)  {
#ifdef AZTEC_C
      if (cp_date) file_date(ds,destname);
#endif
      printf("..copied\n");
   }
   else {
      DeleteFile(destname);
      printf("..Not copied..");
   }
fail:
   dclose(dps);
   if (buf)
      FreeMem(buf, 8192L);
   CurrentDir(cwd);
   return(ierr);
}

#ifdef AZTEC_C		 /* since 3.20a did'nt have dos_packet() */

file_date(date,name)
struct DateStamp *date;
char *name;
{
    UBYTE *ptr;
    struct MsgPort *task;
    struct FileLock *dirlock;
    struct DPTR *tmp; 
    int stat;
    long ret, dos_packet();

    if (!(task = (struct MsgPort *)DeviceProc(name)))
	return(1);
    if (tmp = dopen(name, &stat)) {
	dirlock = (struct FileLock *)ParentDir(tmp->lock);
	ptr = (UBYTE *)AllocMem(64L,MEMF_PUBLIC);
	strcpy((ptr + 1),tmp->fib->fib_FileName);
	*ptr = strlen(tmp->fib->fib_FileName);
	dclose(tmp);
	ret = dos_packet(task,34L,NULL,dirlock,
		         (ULONG)&ptr[0] >> 2L,date);
	FreeMem(ptr,64L);
	UnLock(dirlock);
    }
}

#endif

SHAR_EOF
cat << \SHAR_EOF > execom.c
/*
 * EXECOM.C
 *
 * Matthew Dillon, 10 August 1986
 *    Finally re-written.
 *
 * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87
 *
 *
 */

#include "shell.h"

#define F_EXACT 0
#define F_ABBR	1

#define ST_COND	  0x03
#define ST_NAME	  0x02
#define ST_NOEXP  0x04

int has_wild = 0;		  /* set if any arg has wild card */

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_pwd(), do_rm(), do_mkdir(), do_history();
extern int do_mem(), do_cat(), do_dir(), do_devinfo(), do_inc();
extern int do_foreach(), do_return(), do_if(), do_label(), do_goto();
extern int do_input(), do_ver(), do_sleep(), do_help();
extern int do_strhead(), do_strtail();
extern int do_copy(), date(),  do_ps();
extern int do_forever(), do_abortline();

static struct COMMAND Command[] = {
   do_run      , 0,  0,		 0 ,   "\001",
   do_number   , 0,  0,		 0 ,   "\001",
   do_set_var  , 0,  0, LEVEL_ALIAS,   "alias",
   do_abortline, 0,  0,		 0,    "abortline",
   do_cd       , 0,  0,		 0 ,   "cd",
   do_cat      , 0,  0,		 0 ,   "cat",
   do_copy     , 1,  0,		 0 ,   "copy",
   date	       , 0,  0,		 0 ,   "date",
   do_dir      , 0,  ST_NOEXP,	 0 ,   "dir",
   do_inc      , 1,  0,		-1 ,   "dec",
   do_devinfo  , 0,  0,		 0 ,   "devinfo",
   do_echo     , 0,  0,		 0 ,   "echo",
   do_if       , 0,  ST_COND,	 1 ,   "else",
   do_if       , 0,  ST_COND,	 2 ,   "endif",
   do_foreach  , 3,  0,		 0 ,   "foreach",
   do_forever  , 1,  0,		 0 ,   "forever",
   do_goto     , 1,  0,		 0 ,   "goto",
   do_help     , 0,  0,		 0 ,   "help",
   do_history  , 0,  0,		 0 ,   "history",
   do_if       , 1,  ST_COND,	 0 ,   "if",
   do_inc      , 1,  0,		 1 ,   "inc",
   do_input    , 1,  0,		 0 ,   "input",
   do_label    , 1,  ST_COND,	 0 ,   "label",
   do_mem      , 0,  0,		 0 ,   "mem",
   do_mkdir    , 0,  0,		 0 ,   "mkdir",
   do_mv       , 2,  0,		 0 ,   "mv",
   do_ps       , 0,  0,		 0,    "ps",
   do_pwd      , 0,  0,		 0 ,   "pwd",
   do_quit     , 0,  0,		 0 ,   "quit",
   do_return   , 0,  0,		 0 ,   "return",
   do_rm       , 0,  0,		 0 ,   "rm",
   do_run      , 1,  ST_NAME,	 0 ,   "run",
   do_set_var  , 0,  0, LEVEL_SET  ,   "set",
   do_sleep    , 0,  0,		 0,    "sleep",
   do_source   , 0,  0,		 0 ,   "source",
   do_strhead  , 3,  0,		 0 ,   "strhead",
   do_strtail  , 3,  0,		 0 ,   "strtail",
   do_unset_var, 0,  0, LEVEL_ALIAS,   "unalias",
   do_unset_var, 0,  0, LEVEL_SET  ,   "unset",
   do_ver      , 0,  0,		 0 ,   "version",
   '\0'	       , 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;
   char buf[32];

   if (!H_stack) {
      add_history(base);
      sprintf(buf, "%d", H_tail_base + H_len);
      set_var(LEVEL_SET, V_HISTNUM, buf);
   }
   scr = malloc((strlen(base) << 2) + 2);    /* 4X */
   preformat(base, scr);
   i = fcomm(scr, 1);
   return ((i) ? -1 : 1);
}

isalphanum(c)
char 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 '#':
	 d[di++] = '\0';
	 while (s[si])
	    ++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) {
      fprintf (stderr,"PREFORMAT: %d :%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 stdout flag for append
 *	       '<'	   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;
{
   static int alias_count;
   int p_alias_count = 0;
   char *istr;
   char *nextstr;
   char *command;
   char *pend_alias = NULL;
   char err = 0;
   has_wild = 0;
   ++alias_count;

   mpush_base();
   if (*str == 0)
      goto done1;
step1:
   if (alias_count == MAXALIAS || ++p_alias_count == MAXALIAS) {
      fprintf(stderr,"Alias Loop\n");
      err = 20;
      goto done1;
   }
   if (*str == '$') {
      if (istr = get_var (LEVEL_SET, str + 1))
	 str = format_insert_string(str, istr, &freeok);
   }
   istr = NULL;
   if (*(unsigned char *)str < 0x80)
      istr = get_var (LEVEL_ALIAS, str);  /* only if not \command */
   *str &= 0x7F;			  /* remove \ teltail	  */
   if (istr) {
      if (*istr == '%') {
	 pend_alias = istr;
      } else {
	 str = format_insert_string(str, istr, &freeok);
	 goto step1;
      }
   }
   if (*str == '!') {
      char *p, c;		      /* fix to allow !cmd1;!cmd2 */
      for(p = str; *p && *p != ';' ; ++p);
      c = *p;
      *p = '\0';
      istr = get_history(str);
      *p = c;
      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) {
      while (elast && elast != ';' && elast != '|')
	 exarg(&nextstr);
      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] = '\0';
      cont = 1;
      doexpand = redir = inc = 0;

      while (cont && elast) {
	 int ccno = find_command(command);
	 ptr = exarg(&nextstr);
	 inc = 1;
	 arg = "";
	 cont = (elast == 0x80);
	 switch (*ptr) {
	 case '<':
	    redir = -2;
	 case '>':
	    if (Command[ccno].stat & ST_NAME) {
							/* don't extract   */
		redir = 0;				/* <> stuff if its */
		arg = ptr;				/* external cmd.   */
		break;
	    }
	    ++redir;
	    arg = ptr + 1;
	    if (*arg == '>') {
	       redir = 2;	 /* append >> (not impl yet) */
	       ++arg;
	    }
	    cont = 1;
	    break;
	 case '$':
	    if ((arg = get_var(LEVEL_SET, ptr + 1)) == NULL)
	       arg = ptr;
	    break;
	 case '*':
	 case '?':
	    if ((Command[ccno].stat & ST_NOEXP) == 0)
	       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;
	 has_wild = 1;
	 eav = ebase = expand(av[ac], &eac);
	 inc = 0;
	 if (eav) {
	    if (ac + eac + 2 > MAXAV) {
	       ierror (NULL, 506);
	       err = 1;
	    } else {
	       QuickSort(eav, eac);
	       for (; eac; --eac, ++eav)
		  av[ac++] = strcpy(mpush(strlen(*eav)+1), *eav);
	    }
	    free_expand (ebase);
	 }
      }

      /* process redirection  */

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

	 if (redir < 0)
	    Cin_name = file;
	 else {
	    Cout_name = file;
	    Cout_append = (redir == 2);
	 }
	 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] = '\0';

   /* 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;
   }


   if (err)
      goto done0;

   {
      register int i, len;
      char save_elast;
      register 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) {
	     fprintf (stderr, "AV[%2d] %d :%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;
	 long  oldcin = (long)Input();
	 long  oldcout = (long)Output();

#ifndef AZTEC_C
	 extern struct _dev _devtab[];
#endif
	 struct _dev *stdfp;

	 fflush(stdout);
	 ccno = find_command (command);
	 if ((Command[ccno].stat & ST_NAME) == 0) {
	    if (Cin_name) {
	       if ((Cin = (long)Open(Cin_name,1005L)) == 0L) {
		  ierror (NULL, 504);
		  err = 1;
		  Cin_name = '\0';
	       } else {
		  Myprocess->pr_CIS = Cin;
		  _devtab[stdin->_unit].fd = Cin;
	       }
	    }
	    if (Cout_name) {
	       if (Cout_append) {
		  if ((Cout = (long)Open(Cout_name, 1005L)) != 0L)
		     Seek(Cout, 0L, 1L);
	       } else {
		  Cout = (long)Open(Cout_name,1006L);
	       }
	       if (Cout == NULL) {
		  err = 1;
		  ierror (NULL, 504);
		  Cout_name = '\0';
		  Cout_append = 0;
	       } else {
		  Myprocess->pr_COS = Cout;
		  _devtab[stdout->_unit].fd = Cout;
	       }
	    }
	 }
	 if (ac < Command[ccno].minargs + 1) {
	    ierror (NULL, 500);
	    err = -1;
	 } else if (!err) {
	    i = (*Command[ccno].func)(avline, Command[ccno].val);
	    if (i < 0)
	       i = 20;
	    err = i;
	 }
	 free (avline);
	 if (Exec_ignoreresult == 0 && Lastresult != err) {
	    Lastresult = err;
	    seterr();
	 }
	 if ((Command[ccno].stat & ST_NAME) == 0) {
	    if (Cin_name) {
	       fflush(stdin);
	       clearerr(stdin);
	       Close(Cin);
	    }
	    if (Cout_name) {
	       fflush(stdout);
	       clearerr(stdout);
	       stdout->_flags &= ~_DIRTY;    /* because of nil: device */
	       Close(Cout);
	       Cout_append = 0;
	    }
	 }

	 /* the next few lines solve a bug with fexecv and bcpl programs */
	 /* that muck up the input/output streams  which causes GURUs	*/

	 Myprocess->pr_CIS =  _devtab[stdin->_unit].fd = oldcin;
	 Myprocess->pr_COS =  _devtab[stdout->_unit].fd = oldcout;
      }
      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 = '\0';
      }
      Cout_name = '\0';
      Cout_ispipe = 0;
      elast = save_elast;
   }
   mpop_tobase();		       /* free arguments   */
   mpush_base();		       /* push dummy base  */

done0:
   {
      char *str;
      if (err && E_stack == 0) {
	 str = get_var(LEVEL_SET, V_EXCEPT);
	 if (err >= ((str)?atoi(str):1)) {
	    if (str) {
	       ++H_stack;
	       ++E_stack;
	       exec_command(str);
	       --E_stack;
	       --H_stack;
	    } else {
	       Exec_abortline = 1;
	    }
	 }
      }
      if (elast != 0 && Exec_abortline == 0)
	 err = fcomm(nextstr, 0);
      Exec_abortline = 0;
      if (Cin_name)
	 DeleteFile(Cin_name);
      Cin_name = NULL;
      Cin_ispipe = 0;
   }
done1:
   mpop_tobase();
   if (freeok)
      free(str);
   --alias_count;
   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 + 2);   /* may need 2 extra bytes in do_run() */
   *(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 = (unsigned char *)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;
   int i= 0;


   for (com = &Command[2]; com->func; ++com) {
      printf ("%-12s", com->name);
      if (++i  % 6 == 0) printf("\n");
   }
   printf("\n");
   return(0);
}

SHAR_EOF
cat << \SHAR_EOF > rawconsole.c
/*
 * RawConsole.c
 *
 * Shell 2.06M	28-May-87
 * console handling, command line editing support for Shell
 * using new console packets from 1.2.
 * Written by Steve Drew. (c) 14-Oct-86.
 * 16-Dec-86 Slight mods to rawgets() for Disktrashing.
 *
 */

extern int aux; /* for use with aux: */

#include "shell.h"

void
setraw(onoff)
{
    if (onoff) set_raw();
    else set_con();
}

char *
rawgets(line,prompt)
char *line, *prompt;
{
    char *get_var();
    char *gets();
    register int n, pl;
    register int max, i;
    unsigned char c1,c2,c3;
    char fkeys[5];
    char *s;
    int fkey;
    int insert = 1;
    char rep[14];
    static int width;
    int recall = -1;
    struct HIST *hist;

    if (aux) {
	printf("%s",prompt);
	fflush(stdout);
    }
    if (!IsInteractive(Input()) || aux ) return(gets(line));
    if (WaitForChar((long)Input(), 100L) ||   /* don't switch to 1L ...*/
	   stdin->_bp < stdin->_bend) {	    /* else causes read err's*/
    /*	printf("%s",prompt); */
	gets(line);
	return(line);
    }
    setraw(1);
    printf("%s",prompt);
    max = pl = i = strlen(prompt);
    strcpy(line,prompt);
    if (!width) width = 77;
    if (s = get_var (LEVEL_SET, "_insert"))
	insert = atoi(s) ? 1 : 0;

    while((c1 = getchar()) != 255) {
	if (c1 < 156) switch(c1) {
	    case 155:
		 c2 = getchar();
		 switch(c2) {
		     case 'A':			/* up arrow   */
			n = ++recall;
		     case 'B':			/* down arrow */
			line[pl] = '\0';
			if (recall >= 0 || c2 == 'A') {
			    if (c2 == 'B') n = --recall;
			    if (recall >= 0) {
				for(hist = H_head; hist && n--;
				    hist = hist->next);
				if (hist) strcpy(&line[pl],hist->line);
				else recall = H_len;
			    }
			}
			if (i != pl)
			    printf("\233%dD",i);
			printf("\015\233J%s",line);
			i = max = strlen(line);
			break;
		     case 'C':			/* right arrow*/
			if (i < max) {
			    i++;
			    printf("\233C");
			}
			break;
		     case 'D':			/* left arrow */
			if (i > pl) {
			    i--;
			    printf("\233D");
			}
			break;
		    case 'T':			/* shift up   */
		    case 'S':			/* shift down */
			break;
		    case ' ':			/* shift -> <-*/
			c3 = getchar();
			break;
		    default:
			c3 = getchar();
			if (c3 == '~') {
			    fkey = c2;
			    fkeys[0] = 'f';
			    if (c2 == 63) {
				strcpy(&line[pl],"help");
				goto done;
			    }
			}
			else if (getchar() != '~') { /* window was resized */
			    while(getchar() != '|');
			    printf("\2330 q"); /* get window bounds */
			    n = 0;
			    while((rep[n] = getchar()) != 'r' && n++ < 14 );
			    width = (rep[n-3] - 48) * 10 + rep[n-2] - 48;
			    rep[n-1] = '\0';
			    set_var (LEVEL_SET, "_width", &rep[n-3]);
			    break;
			}
			else {
			    fkey = c3;
			    fkeys[0] = 'F';
			}
			sprintf(fkeys+1,"%d",fkey - 47);
			if (!(s = get_var(LEVEL_SET, fkeys))) break;
			strcpy(&line[pl], s);
			printf("%s",&line[pl]);
			goto done;
			break;
		    }
		break;
	    case 8:
		if (i > pl) {
		    i--;
		    printf("\010");
		}
		else break;
	    case 127:
		if (i < max) {
		    int j,t,l = 0;
		    movmem(&line[i+1],&line[i],max-i);
		    --max;
		    printf("\233P");
		    j = width - i % width - 1;	 /* amount to end     */
		    t = max/width - i/width;	 /* no of lines	      */
		    for(n = 0; n < t; n++) {
			l += j;			 /* no. of char moved */
			if (j) printf("\233%dC",j); /* goto eol	      */
			printf("%c\233P",line[width*(i/width+n+1)-1]);
			j = width-1;
		    }
		    if (t)
		    printf("\233%dD",l+t);   /* get back */
		}
		break;
	    case 18:
		n = i/width;
		if (n) printf("\233%dF",n);
		printf("\015\233J%s",line);
		i = max;
		break;
	    case 27:
	    case 10:
		break;
	    case 1:
		insert ^= 1;
		break;
	    case 21:
	    case 24:
	    case 26:
		if (i > pl)
		    printf("\233%dD",i-pl);
		i = pl;
		if (c1 == 26) break;
		printf("\233J");
		max = i;
		line[i] = '\0';
		break;
	    case 11:	    /* ^K */
		printf("\233J");
		max = i;
		line[i] = '\0';
		break;
	    case 28:	    /* ^\ */
		setraw(0);
		return(NULL);
	    case 5:
		printf("\233%dC",max - i);
		i = max;
		break;
	    case 13:
		line[max] = '\0';
done:		printf("\233%dC\n",max - i);

		setraw(0);
		strcpy(line, &line[pl]);
		return(line);
	    default:
		if (c1 == 9) c1 = 32;
		if (c1 > 31 & i < 256) {
		    if (i < max && insert) {
			int j,t,l = 0;
			movmem(&line[i], &line[i+1], max - i);
			printf("\233@%c",c1);
			t = max/width - i/width;
			j = width - i % width - 1;
			for(n = 0; n < t; n++) {
			    l += j;
			    if (j) printf("\233%dC",j);
			    printf("\233@%c",line[width*(i/width+n+1)]);
			    j = width-1;
			}
			if (t) printf("\233%dD",l + t);
			++max;
		    }
		    else {
			if (i == pl && max == i) printf("\015%s",line);
			putchar(c1);
		    }
		    line[i++] = c1;
		    if (max < i) max = i;
		    line[max] = '\0';
		}
	}
    }
    setraw(0);
    return(NULL);
}

SHAR_EOF
cat << \SHAR_EOF > run.c

/*
 * RUN.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 *    RUN   handles running of external commands.
 *
 * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87
 *
 */

#include "shell.h"

char *FindIt();

do_run(str)
char *str;
{
   int i, try = -1;
   int run = 0;
   char buf[128];
   char runcmd[128];
   char *save, *path, *index();

   char *p = av[0];
   char **args = av+1;
   
   while(*p++) *p &= 0x7F;	/* allow "com mand" */

   while(*args) {                  /* if any arg contains a space then */
      if (index(*args,' ')) {      /* surround with quotes, since must */
         i = strlen(*args);        /* of specified via "arg u ment" on */
	 movmem(*args,(*args)+1,i);/* original command line.           */
         args[0][0] = args[0][i+1] = '\"';  /* mpush in execom.c has   */
         args[0][i+2] = '\0';      /* allowed for these 2 extra bytes. */
      }
      ++args;
   }
   
   if (path = FindIt(av[0],"",buf)) {
      if (!strcmp(av[0],"run") || !strcmp(av[0],"ru")) {      /* was a run */
	 if (FindIt(av[1],"",runcmd)) {
	    run = 1;
	    save = av[1];
	    av[1] = runcmd;
	 }
      }
      if ((try = fexecv(path, av)) == 0)
	 i = wait();
      if (run) av[1] = save;
   }
   else {
      APTR original;
      original = Myprocess->pr_WindowPtr;
      Myprocess->pr_WindowPtr = (APTR)(-1);
      if ((try = fexecv(av[0], av)) == 0)
	 i = wait();
      Myprocess->pr_WindowPtr = original;
   }
   if (try) {
      long lock;
      char *copy;

      if ((path = FindIt(av[0],".sh",buf)) == NULL) {
	 fprintf(stderr,"Command Not Found %s\n",av[0]);
	 return (-1);
      }
      av[1] = buf;		 /* particular to do_source() */
      copy = malloc(strlen(str)+3);
      strcpy(copy+2,str);
      copy[0] = 'x';
      copy[1] = ' ';
      i = do_source(copy);
      free(copy);
   }
   return (i);
}


char *
FindIt(cmd, ext, buf)
char *cmd;
char *ext;
char *buf;
{
   long lock = 0;
   char hasprefix = 0;
   APTR original;
   char *ptr, *s = NULL;

   original = Myprocess->pr_WindowPtr;

   for (ptr = cmd; *ptr; ++ptr) {
      if (*ptr == '/' || *ptr == ':')
	 hasprefix = 1;
   }

   if (!hasprefix) {
	Myprocess->pr_WindowPtr = (APTR)(-1);
	s = get_var(LEVEL_SET, V_PATH);
   }

   strcpy(buf, cmd);
   strcat(buf, ext);
   while ((lock = (long)Lock(buf, ACCESS_READ)) == 0) {
      if (*s == NULL || hasprefix) break;
      for(ptr = s; *s && *s != ','; s++) ;
      strcpy(buf, ptr);
      buf[s-ptr] = '\0';
      strcat(buf, cmd);
      strcat(buf, ext);
      if (*s) s++;
   }
   Myprocess->pr_WindowPtr = original;
   if (lock) {
      UnLock(lock);
      return(buf);
   }
   return(NULL);
}
SHAR_EOF
cat << \SHAR_EOF > shell.h

/*
 * SHELL.H
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 *
 * SHELL include file.. contains shell parameters and extern's
 *
 * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87
 *
 */

#include <stdio.h>
#include <time.h>
#include <exec/types.h>
#include <exec/exec.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <exec/memory.h>
#include <exec/tasks.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <functions.h>
#include <fcntl.h>

#define bmov   movmem
#define STDBUF 1

#define MAXAV	     256	    /* Max. # arguments		    */
#define MAXSRC	     5		    /* Max. # of source file levels */
#define MAXIF	     10		    /* Max. # of if levels	    */
#define MAXALIAS     20		    /* Max. # of alias levels	    */

#define LEVEL_SET    0		    /* which variable list to use   */
#define LEVEL_ALIAS  1
#define LEVEL_LABEL  2

#define V_PROMPT     "_prompt"	    /* your prompt (ascii command)   */
#define V_HIST	     "_history"	    /* set history depth (value)     */
#define V_HISTNUM    "_histnum"	    /* set history numbering var     */
#define V_DEBUG	     "_debug"	    /* set debug mode		     */
#define V_VERBOSE    "_verbose"	    /* set verbose for source files  */
#define V_STAT	     "_maxerr"	    /* worst return value to date    */
#define V_LASTERR    "_lasterr"	    /* return value from last comm.  */
#define V_CWD	     "_cwd"	    /* current directory	     */
#define V_EXCEPT     "_except"	    /* "nnn;command"		     */
#define V_PASSED     "_passed"	    /* passed arguments to source fle*/
#define V_PATH	     "_path"	    /* search path for external cmds */

	    /* EXECOM.C defines */

#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.06M  (c)1986 Matthew Dillon.  Manx version by Steve Drew"

#ifndef NULL
#define NULL 0L
#endif

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

#ifndef AZTEC_C
struct _dev	 {
		long  fd;
		short mode;
	};
#endif

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();
extern char **expand();
extern char *av[];
extern char *Current;
extern int  H_len, H_tail_base, H_stack;
extern int  E_stack;
extern int  Src_stack, If_stack;
extern int  ac;
extern int  debug, Rval, Verbose, disable, Quit;
extern int  Lastresult;
extern int  Exec_abortline, Exec_ignoreresult;
extern int   S_histlen;
extern long  Uniq;
extern long  Cin, Cout, Cout_append;
extern char *Cin_name, *Cout_name;
extern char  Cin_type,	Cout_type;  /* these variables are in transition */
extern char *Pipe1, *Pipe2;

extern long Src_base[MAXSRC];
extern long Src_pos[MAXSRC];
extern char If_base[MAXIF];
extern struct Process *Myprocess;
SHAR_EOF