[comp.sources.amiga] v02i042: Shell 2.07M

drew@decwrl.dec.com (Steve Drew) (10/08/87)

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	execom.c
#	globals.c
#	main.c
#	makefile
#	run.c
#	set.c
#	shell.h
#	sort.c
#	sub.c
# This archive created: Fri Oct  2 09:11:38 1987
echo shar: extracting execom.c
cat << \SHAR_EOF > execom.c
/*
 * EXECOM.C
 *
 * Matthew Dillon, 10 August 1986
 *    Finally re-written.
 *
 * Version 2.07M by Steve Drew 10-Sep-87
 *
 *
 */
 
#include "shell.h"
 
#define F_EXACT 0
#define F_ABBR	1
 
#define ST_COND	  0x01
#define ST_NORED  0x02
#define ST_NOEXP  0x04
#define ST_AV     0x08 /* delimit args within a variable */
 
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();
char *push_cpy();
 
static struct COMMAND Command[] = {
   do_run      , 0,  ST_AV,      0,   "\001",   /* may call do_source */
   do_number   , 0,  0,		 0 ,   "\001",
   do_set_var  , 0,  0, LEVEL_ALIAS,   "alias",  /* uses avline */
   do_abortline, 0,  0,		 0,    "abortline",
   do_cat      , 0,  0,		 0 ,   "cat",
   do_cd       , 0,  0,		 0 ,   "cd",
   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",  /* uses avline */
   do_if       , 0,  ST_COND,	 1 ,   "else",
   do_if       , 0,  ST_COND,	 2 ,   "endif",
   do_foreach  , 3,  ST_NORED,	 0 ,   "foreach",
   do_forever  , 1,  ST_NORED,	 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,  ST_NORED,	 0 ,   "quit",
   do_return   , 0,  0,		 0 ,   "return",
   do_run      , 1,  ST_NORED,   0,    "run",    
   do_rm       , 0,  0,		 0 ,   "rm",
   do_set_var  , 0,  ST_AV, LEVEL_SET, "set",
   do_sleep    , 0,  0,		 0,    "sleep",
   do_source   , 0,  ST_NORED|ST_AV, 0,"source", /* uses avline */
   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) {
      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[1] == '$') {
      if (istr = get_var (LEVEL_SET, str + 2)) 
         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) {
      if (cmd_stat(command) & ST_COND)
	 goto skipgood;
   }
   if (disable || forward_goto) {
      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 cstat = cmd_stat(command);
 
	 ptr = exarg(&nextstr);
	 inc = 1;
	 arg = "";
	 cont = (elast == 0x80);
	 switch (*ptr) {
	 case '<':
	    redir = -2;
	 case '>':
	    if (cstat & (ST_NORED | ST_COND)) {
							/* don't extract   */
		redir = 0;				/* <> stuff if its */
		arg = ptr;				/* external cmd.   */
		break;
	    }
	    ++redir;
	    arg = ptr + 1;
	    if (*arg == '>') {
	       redir = 2;	 /* append >> */
	       ++arg;
	    }
	    cont = 1;
	    break;
	 case '$':
	    /* restore args if from set command or pend_alias */
	    if ((arg = get_var(LEVEL_SET, ptr + 1)) != NULL) {
	       if (cstat & ST_COND) {
	          char *tp;
                  tp = push_cpy(arg); 
                  arg = tp;
               }
               else {
                  char *pe, sv, *index();
	          while (pe = index(arg,0xA0)) {
                     sv = *pe;
	             *pe = '\0';
		     av[ac++] = push_cpy(arg); 
                     *pe = sv;
		     av[ac] = '\0';
		     arg = pe+1;
		  }
	       }
	    }
	    else
	       arg = ptr;
	    break;
	 case '*':
	 case '?':
	    if ((cstat & 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)+strlen(av[ac]));
	    strcpy(av[ac], old);
	    strcat(av[ac], arg);
	 } else {
	    av[ac] = push_cpy(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++] = push_cpy(*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;
      char *compile_av();
      register char *avline;
      unsigned char delim = ' ';
 
      save_elast = elast;
      if (pend_alias || (cmd_stat(command) & ST_AV)) 
 	 delim = 0xA0;
      avline = compile_av(av,((pend_alias) ? 1 : 0), ac , delim);
 
 
      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  = Myprocess->pr_CIS;
	 long  oldcout = Myprocess->pr_COS;
	 char *Cin_buf;
      	 struct FileHandle *ci;
	 long oldbuf;
	 struct _dev *stdfp;
 
	 fflush(stdout);
	 ccno = find_command (command);
	 if ((Command[ccno].stat & (ST_NORED | ST_COND)) == 0) {
	    if (Cin_name) {
	       if ((Cin = (long)Open(Cin_name,1005L)) == 0L) {
		  ierror (NULL, 504);
		  err = 1;
		  Cin_name = '\0';
	       } else {
		  Myprocess->pr_CIS = _devtab[stdin->_unit].fd = Cin;
	          ci = (struct FileHandle *)(((long)Cin)<<2);
		  Cin_buf = (char *)AllocMem(202L, MEMF_PUBLIC);
		  oldbuf = ci->fh_Buf;
		  if (ci->fh_Buf == 0) /* fexec expects a CIS buffer */
		     ci->fh_Buf = (long)Cin_buf>>2;
	       }
	    }
	    if (Cout_name) {
	       if (Cout_append && (Cout =(long)Open(Cout_name, 1005L)) ) {
		     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 = _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 (E_stack == 0 && Lastresult != err) {
	    Lastresult = err;
	    seterr();
	 }
	 if ((Command[ccno].stat & (ST_NORED | ST_COND)) == 0) {
	    if (Cin_name) {
	       fflush(stdin);
	       clearerr(stdin);
	       ci->fh_Buf = oldbuf;
	       Close(Cin);
	       FreeMem(Cin_buf, 202L);
	    }
	    if (Cout_name) {
	       fflush(stdout);
	       clearerr(stdout);
	       stdout->_flags &= ~_DIRTY;    /* because of nil: device */
	       Close(Cout);
	       Cout_append = 0;
	    }
	 }
         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(6 + bytes + 2);   /* may need extra 2 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);
}
 
cmd_stat(str)
char *str;
{
   return(Command[find_command(str)].stat);
}
 
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);
}
 
 
char *
push_cpy(s)
char *s;
{
   return(strcpy(mpush(strlen(s)), s));
}
 
 
SHAR_EOF
if test 16332 -ne "`wc -c execom.c`"
then
echo shar: error transmitting execom.c '(should have been 16332 characters)'
fi
echo shar: extracting globals.c
cat << \SHAR_EOF > globals.c
 
/*
 * GLOBALS.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 * Version 2.07M by Steve Drew 10-Sep-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", Can't detect this at the present */ 
   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",
 
   /* 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",
   509,  "Error in command name",
     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   forward_goto;	      /* Flag for searching for foward lables   */
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   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
if test 2934 -ne "`wc -c globals.c`"
then
echo shar: error transmitting globals.c '(should have been 2934 characters)'
fi
echo shar: extracting main.c
cat << \SHAR_EOF > main.c
 
/*
 * MAIN.C
 *
 * Matthew Dillon, 24 Feb 1986
 * (c)1986 Matthew Dillon     9 October 1986
 *
 * Version 2.07M by Steve Drew 10-Sep-87
 *
 */
 
#include "shell.h"
 
int aux; /* for use with aux: driver */
 
char Inline[260];
 
main(argc, argv)
register char *argv[];
{
#if RAW_CONSOLE
   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(Lastresult);
	    }
      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 (disable)
         prompt = "_ ";
      if (breakcheck()) {
	 while (WaitForChar(Input(), 100L) || stdin->_bp < stdin->_bend)
	    gets(Inline);
      }
      clearerr(stdin);	/* prevent acidental quit */
#if RAW_CONSOLE
      if (Quit || !rawgets(Inline, prompt))
#else
      printf("%s",prompt);
      fflush(stdout);
      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 */
 
#if RAW_CONSOLE
   printf("\23312{");		   /* enable window resize reports */
#endif
   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       */
 
   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);
}
 
dobreak()
{
   if (breakcheck()) {
       printf("^C\n");
       return(1);
   }
   return(0);
}
 
/* 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
if test 2977 -ne "`wc -c main.c`"
then
echo shar: error transmitting main.c '(should have been 2977 characters)'
fi
echo shar: extracting makefile
cat << \SHAR_EOF > makefile
######################################################################
#
# Makefile to build Shell 2.07M
# 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
if test 862 -ne "`wc -c makefile`"
then
echo shar: error transmitting makefile '(should have been 862 characters)'
fi
echo shar: extracting run.c
cat << \SHAR_EOF > run.c
 
/*
 * RUN.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 *    RUN   handles running of external commands.
 *
 * Version 2.07M by Steve Drew 10-Sep-87
 *
 */
 
#include "shell.h"
 
char *FindIt();
 
do_run(str)
char *str;
{
   int i, len, try = -1;
   int run = 0;
   char buf[200]; /* enough space for 100 char cmd name + path stuff */
   char runcmd[200];
   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 ((len = strlen(av[0])) > 100) {
   	ierror(NULL,509);
	return(-1);
   }   
   if (path = FindIt(av[0],"",buf)) {
      if (!strcmp(av[0],"run")) {      	/* 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);
      /*
       * manx's fexev code only allows us 38
       * chars for command name. 
       */
      if (len > 37) av[0][37] = '\0'; 
      if ((try = fexecv(av[0], av)) == 0)
	 i = wait();
	 
      Myprocess->pr_WindowPtr = original;
   }
   if (try) {
      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
if test 2876 -ne "`wc -c run.c`"
then
echo shar: error transmitting run.c '(should have been 2876 characters)'
fi
echo shar: extracting set.c
cat << \SHAR_EOF > set.c
 
/*
 * SET.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 * Version 2.07M by Steve Drew 10-Sep-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]);
   sys_vars();
   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)));
     }
   sys_vars();
   return (0);
}
 
sys_vars()
{
   char *str;
   
   if (*av[1] == '_') {
      S_histlen = (str = get_var(LEVEL_SET, V_HIST))   ? atoi(str) : 0;
      debug	= (str = get_var(LEVEL_SET, V_DEBUG))  ? (*str != '\0') : 0;
      Verbose = (str = get_var(LEVEL_SET, V_VERBOSE)) ? (*str != '\0') : 0;
      if (S_histlen < 2)   S_histlen = 2;
   }
}
SHAR_EOF
if test 3650 -ne "`wc -c set.c`"
then
echo shar: error transmitting set.c '(should have been 3650 characters)'
fi
echo shar: extracting shell.h
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.07M by Steve Drew 10-Sep-87
 *
 */
 
#define RAW_CONSOLE 1	/* Set to 0 to compile out Cmd Line Editing */
 
#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 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 */
#define V_GOTOFWD    "_gtf"	    /* set name for fwd goto name    */
 
            /* 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.07M (c)1986 Matthew Dillon. Manx (M) versions by Steve Drew"
 
#ifndef NULL
#define NULL 0L
#endif
 
/* #define CHECKBREAK() ( breakcheck() ? (printf("^C\n"),1) : 0) */
#define CHECKBREAK() dobreak()
 
#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, forward_goto;
extern int  ac;
extern int  debug, Rval, Verbose, disable, Quit;
extern int  Lastresult;
extern int  Exec_abortline;
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
if test 3823 -ne "`wc -c shell.h`"
then
echo shar: error transmitting shell.h '(should have been 3823 characters)'
fi
echo shar: extracting sort.c
cat << \SHAR_EOF > sort.c
 
/*
 * sort.c
 *
 *
 * Version 2.07M by Steve Drew 10-Sep-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
if test 1382 -ne "`wc -c sort.c`"
then
echo shar: error transmitting sort.c '(should have been 1382 characters)'
fi
echo shar: extracting sub.c
cat << \SHAR_EOF > sub.c
 
/*
 * SUB.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 * Version 2.07M by Steve Drew 10-Sep-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 && (unsigned char)*str != 0xA0)
      ++str;
   while (*str	&& (*str == ' ' || *str == 9 || (unsigned char)*str == 0xA0))
      ++str;
   return (str);
}
 
char *
compile_av(av, start, end, delim)
char **av;
unsigned char delim;
{
   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) {
      if (debug) {
	 fprintf (stderr, "AV[%2d] :%s:\n", i, av[i]);
      }
      strcat (cstr, av[i]);
      if (i + 1 < end)
         strncat(cstr, &delim, 1);
   }
   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 %d %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'arwed   <Dir>       DD-MMM-YY HH:MM:SS\n'\0'
 *    fullfilename'\0'arwed  NNNNNN NNNN  DD-MMM-YY HH:MM:SS\n'\0'
 *		      01234567890123456789012345678901234567 8 9
 *
 */
formatfile(str,fib)
char *str;
register struct FileInfoBlock *fib;
{
   char *dates();
 
   while(*str++);
 
         
   *str++ = (fib->fib_Protection & FIBF_ARCHIVE) 	? 'a' : ' ';
   *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
if test 13078 -ne "`wc -c sub.c`"
then
echo shar: error transmitting sub.c '(should have been 13078 characters)'
fi
#	End of shell archive
exit 0