[net.sources] C-Shell for the PC

mr@isrnix.UUCP (michael regoli) (11/21/85)

]:[

what follows is the source code for the recently-posted "shell.exe" to
net.micro.pc.  there have been many requests for kent's sources...*so*
many, i had to post them.  

this package is public-domain and was downloaded from a local rbbs.

for the UUENCODED(1C) version, please see the corrected version in 
net.micro.pc.  the documentation is there also.

the sources that follow have been successfully compiled using the AZTEC-C
compiler.

-- CHOP CHOP --

#########################################################
#                                                       #
# This is a shell archive file.  To extract files:      #
#                                                       #
#    1)	Make a directory for the files.                 #
#    2) Write a file, such as "file.shar", containing   #
#       this archive file into the directory.           #
#    3) Type "sh file.shar".  Do not use csh.           #
#                                                       #
#########################################################
#
echo Extracting _croot.c:
sed 's/^Z//' >_croot.c <<\STUNKYFLUFF
Z/* Copyright (C) 1981,1982, 1983 by Manx Software Systems */
Z#include <stdio.h>
Z#include <errno.h>
Z#include <fcntl.h>
Z#ifndef NULL
Z#define NULL ((void *)0)
Z#endif
Z
Zchar *get_first(), *get_next(), *sbrk();
Zchar *wilderr = "No wild cards in command names!!\r\n";
Z
Z/* noexpand can be set by routines before calling _Croot */
Zint noexpand = 0;
Z
Z#define ARGMAX 256
Zstatic char *Argv[ARGMAX];
Zstatic int Argc;
Zstatic char curr_path[128];
Z
Z_Croot(cp,cmd)
Zregister char *cp;
Zint (*cmd)();
Z{
Z	int returnval = 0;
Z	char *startbuf,*endbuf;
Z	register char *cp2;
Z	char *wild_match;
Z	char *index(),*rindex(), *save_str();
Z	char *path,*copy; int j;
Z	char *quote;
Z	int k,omode; char *fname;
Z	int in = -1, out = -1;
Z	/* lets try not to free things not allocated by malloc */
Z	startbuf = cp; endbuf = &cp[strlen(cp)];
Z	if (!noexpand)
Z	{
Z		/* ls is a special case !!! */
Z		if(0 == strncmp(cp,"ls",2) || 0 == strncmp(cp,"dir",3))
Z			noexpand++;
Z	}
Z
Z	/* loop through arguments */
Z	for (Argc = 0;;) 
Z	{
Z		/* skip blanks */
Z		while (*cp == ' ' || *cp == '\t')
Z			++cp;
Z
Z		/* if you're at the end of command line, you're done */
Z		if (*cp == 0)
Z			break;
Z		/* handle redirection . . . */
Z		if (*cp == '>')
Z		{
Z			k = 1;
Z			if (cp[1] == '>')
Z			{
Z				++cp;
Z				omode = O_CREAT | O_WRONLY | O_APPEND; 
Z			}
Z			else
Z				omode = O_CREAT | O_WRONLY | O_TRUNC;
Z			goto redirect;
Z		} else if (*cp == '<')
Z		{
Z			k = 0;
Z	redirect:
Z			while (*++cp == ' ' || *cp == '\t')
Z				;
Z			fname = cp;
Z			while(*++cp)
Z				if (*cp == ' ' || *cp == '\t')
Z				{
Z					*cp++ = 0;
Z					break;
Z				}
Z			close(k);
Z			if (k)
Z				out = k = open(fname,omode);
Z			else
Z				in = k = open(fname,O_RDONLY);
Z			if (k == -1)
Z			{
Z				perror("redirection");
Z				return -1;
Z			}
Z			/* go back for next argument */
Z			continue;
Z		}
Z		/* find beginning of next argument */
Z		cp2 = cp;	/* save original pointer to the string */
Z		while (*++cp2)
Z		{
Z			/* if you hit a space char - stick a null in to terminate last
Z			   argument
Z			 */
Z			if (*cp2 == ' ' || *cp2 == '\t') 
Z			{
Z				*cp2++ = 0;
Z				break;
Z			}
Z		}
Z
Z		/* if no wild card characters, do it the old fashioned way */
Z		if (index(cp,'*') == NULL && index(cp,'?') == NULL)
Z		{
Znotranslate:
Z			if (*cp == '\'')
Z			/* pass through untranslated, with quotes stripped */
Z			{
Z				cp++;	/* point past quote */
Z				if ( NULL == (quote = rindex(cp,'\'')))
Z				{
Z					write(2,"sh - no close quotes on command line\r\n",38);
Z					goto free_args;
Z				}
Z				*quote = '\0';
Z			}
Z			/* update the next argv pointer */
Z			Argv[Argc] = cp;
Z			/* bump the argument count */
Z			if (++Argc == ARGMAX)
Z				abort();
Z		}
Z		else
Z		{
Z			if (*cp == '"' || *cp == '\'')
Z				goto notranslate;
Z			if (noexpand)
Z				goto notranslate;
Z			/* wild cards not permitted on first run thru */
Z			if (Argc == 0)
Z			{
Z				write(2,wilderr,strlen(wilderr));
Z				return -1;
Z			}
Z			/* if there is a path included, save it off */
Z			if ((path = rindex(cp,'\\')) || (path = rindex(cp,'/')))
Z			{
Z				copy = cp;
Z				/* copy to curr_path, mapping / to \ */
Z				for (j = 0; j < sizeof(curr_path) && copy != path+1; copy++,j++)
Z					curr_path[j] = (*copy == '/' ? '\\' : *copy);
Z				/* terminate string */
Z				curr_path[j] = '\0';
Z			}
Z			else if (cp[1] == ':')
Z			{
Z				copy = cp;
Z				for (j = 0; j < 2; j++)
Z					curr_path[j] = *copy++;
Z				curr_path[j] = '\0';
Z			} else
Z			/* null path */
Z				curr_path[0] = 0;
Z			if (wild_match = get_first(cp))
Z			{
Z				/* update the next argv pointer */
Z				Argv[Argc]= save_str(wild_match);
Z				/* bump the argument count */
Z				if (++Argc == ARGMAX)
Z					abort();
Z				/* get the rest of the matching file names */
Z				while (wild_match = get_next())
Z				{
Z					
Z					/* update the next argv pointer */
Z					Argv[Argc] = save_str(wild_match);
Z					/* bump the argument count */
Z					if (++Argc == ARGMAX)
Z						abort();
Z				}
Z			}
Z		}
Z		cp = cp2;	/* point to beginning of next argument */
Z	}
Z	Argv[Argc] = NULL;	
Z	returnval=(*cmd)(Argc,Argv);
Z	if (in != -1)
Z		close(in);
Z	if (out != -1)
Z		close(out);
Z	/* free anything not dynamically allocated */
Zfree_args:
Z	if (!noexpand)
Z	{
Z		for(j = 1;j < Argc; j++)
Z			if (
Z			!(Argv[j] >= startbuf && Argv[j] <= endbuf)	/* not in cmd line */
Z			&& Argv[j]									/* Not NULL			*/
Z		   		)
Z			free(Argv[j]);
Z	}
Z	noexpand = 0;
Z	return returnval;
Z}
Z
Zchar *
Zsave_str(s)
Z	register char *s;
Z{
Z	register char *r,*malloc();
Z	int pathlen;
Z	/* squirrel away matched file name */
Z	if (NULL == (r = malloc(strlen(s)+(pathlen = strlen(curr_path))+1)))
Z		abort();
Z	strcat(curr_path,s);
Z	strcpy(r,curr_path);
Z	curr_path[pathlen] = '\0';
Z	return r;
Z}
Z
Zabort()
Z{
Z	write(2, "Too many args.", 14);
Z	_exit(200);
Z}
Z
Z
Ztypedef struct
Z{
Z	char dos_reserved[21];
Z	char attribute;
Z	unsigned file_time;
Z	unsigned file_date;
Z	long file_size;
Z	char file_name[13];
Z} fcb;
Zfcb wildcard;
Z
Zchar *get_first(fname)
Z	char *fname;
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&wildcard);
Z	result = bdos(0x4E,fname,0);
Z	/* make the find first call */
Z	if(2 == result || 18 == result)
Z		return NULL;
Z	return &(wildcard.file_name[0]);
Z}
Z
Zchar *get_next()
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&wildcard);
Z	result = bdos(0x4f,0,0);
Z	/* make the find next call */
Z	if (18 == result)
Z		return NULL;
Z	return &(wildcard.file_name[0]);
Z}
STUNKYFLUFF
set `sum _croot.c`
if test 54651 != $1
then
echo _croot.c: Checksum error. Is: $1, should be: 54651.
fi
#
#
echo Extracting cat.c:
sed 's/^Z//' >cat.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <signal.h>
Z#include <setjmp.h>
Z
Zvoid (*signal())();
Z
ZFILE *fd1,*fd2;
Z
Zvoid (*oldsig)();
Zchar *fgets();
Zjmp_buf catenv;
Zcatintr()
Z{
Z	signal(SIGINT,SIG_IGN);			/* ignore signals */
Z	fclose(fd1);
Z	fclose(fd2);
Z	signal(SIGINT,oldsig);			/* restore shell interrupt */
Z	longjmp(catenv,-1);
Z}
Z
Zcat(argc,argv)
Z	char *argv[];
Z{
Z	char *intmsg = "Interrupt received\n";
Z
Z	FILE *fdopen(), *fopen();
Z	if (-1==setjmp(catenv))
Z	{
Z		write(2,intmsg,strlen(intmsg));
Z		return -1;
Z	}
Z	oldsig = signal(SIGINT,catintr);	/* trap interrupts from keyboard */
Z	/* get standard output opened for business */
Z	if (NULL == (fd2 = fdopen(1,"w")))
Z	{
Z		perror("cat : Can't open stdout");
Z	}
Z
Z	/* handle pipes */
Z	if (argc == 1)
Z	{
Z		if (NULL == (fd1 = fdopen(0,"r")))
Z		{
Z			perror("cat : Can't open stdin");
Z		}
Z		_cat();
Z		fclose(fd1);fclose(fd2);
Z	}
Z	/* handle specified files */
Z	else
Z	{
Z		while(--argc)
Z		{
Z			if (NULL == (fd1 = fopen(*(++argv),"r")))
Z			{
Z				fprintf(stderr,"can't open %s\n",*argv);
Z				continue;
Z			}
Z			_cat();
Z			fclose(fd1);
Z		}
Z	}
Z	fclose(fd2);
Z	signal(SIGINT,oldsig);				/* restore old int catcher		*/
Z}
Z
Z_cat()
Z{
Z	char buffer[512];
Z	while (NULL != fgets(buffer,512,fd1))
Z		fputs(buffer,fd2);
Z}
STUNKYFLUFF
set `sum cat.c`
if test 12297 != $1
then
echo cat.c: Checksum error. Is: $1, should be: 12297.
fi
#
#
echo Extracting cd.c:
sed 's/^Z//' >cd.c <<\STUNKYFLUFF
Z#include <stdio.h>
Zcd(argc,argv)
Zchar *argv[];
Z{
Z	static char *usage = "usage : cd newdir";
Z	if (argc == 1)
Z		write(2,usage,strlen(usage));
Z	if (-1 == chdir(*(++argv)))
Z	{
Z		perror("cd");
Z		return -1;
Z	}
Z	return 0;
Z}
STUNKYFLUFF
set `sum cd.c`
if test 24433 != $1
then
echo cd.c: Checksum error. Is: $1, should be: 24433.
fi
#
#
echo Extracting chmod.c:
sed 's/^Z//' >chmod.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <stat.h>
Z#ifdef MAIN
Zmain
Z#else
Zch_mod
Z#endif
Z(argc,argv)
Z	char *argv[];
Z{
Z	int or_mask,and_mask,file_stat;
Z	struct { int ax,bx,cx,dx,si,di,ds,es; } regs;
Z	extern int _dsval;
Z	char *current;
Z	regs.ds = _dsval;
Z	if (argc==1)
Z	{
Z		fprintf(stderr,"Usage : chmod +|-[ahw] file [file ...]\n");
Z	}
Z	/* set attributes to default */
Z	or_mask = 0; and_mask = 0xFFFF;
Z	while(--argc)
Z	{
Z		current = *(++argv);
Z		switch (*current)
Z		{
Z		case '-':
Z			while (*++current)
Z			{
Z				switch(*current)
Z				{
Z				case 'w':
Z				case 'W':
Z					or_mask |= ST_RDONLY;
Z					break;
Z				case 'h':
Z				case 'H':
Z					and_mask &= (ST_HIDDEN ^ 0xFFFF);
Z					break;
Z				case 'r':
Z				case 'R':
Z					or_mask |= ST_HIDDEN;
Z					break;
Z				case 'a':
Z				case 'A':
Z					and_mask &= (ST_ARCHIV ^ 0xFFFF);
Z					break;
Z				case 's':
Z				case 'S':
Z					and_mask &= (ST_SYSTEM ^ 0xFFFF);
Z					break;
Z				default:
Z					write(2,"invalid attribute\r\n",19);
Z				return -1;
Z				}
Z			}
Z			break;
Z		case '+':
Z			while(*++current)
Z			{
Z				switch(*current)
Z				{
Z				case 'w':
Z				case 'W':
Z					and_mask &= (ST_RDONLY ^ 0xFFFF);
Z					break;
Z				case 'h':
Z				case 'H':
Z					or_mask |= ST_HIDDEN;
Z					break;
Z				case 's':
Z				case 'S':
Z					or_mask |= ST_SYSTEM;
Z					break;
Z				case 'r':
Z				case 'R':
Z					and_mask &= (ST_HIDDEN ^ 0xFFFF);
Z					break;
Z				case 'a':
Z				case 'A':
Z					or_mask |= ST_ARCHIV;
Z					break;
Z				default:
Z					write(2,"invalid attribute\r\n",19);
Z					return -1;
Z
Z				}
Z			}
Z			break;
Z		default:
Z			/* get current attribute */
Z			regs.ax = 0x4300;
Z			regs.dx = (int)current;
Z			regs.ds = _dsval;
Z			sysint(0x21,&regs,&regs);
Z			file_stat = regs.cx;
Z			fprintf(stderr,"current attribute for %s = %x\n",
Z				current,file_stat);
Z			/* set new attribute */
Z			file_stat |= or_mask;
Z			file_stat &= and_mask;
Z			regs.ax = 0x4301;
Z			regs.dx = (int)current;
Z			regs.cx = file_stat;
Z			regs.ds = _dsval;
Z			sysint(0x21,&regs,&regs);
Z			/* get attribute to see if it changed */
Z			regs.ax = 0x4300;
Z			regs.dx = (int)current;
Z			regs.ds = _dsval;
Z			sysint(0x21,&regs,&regs);
Z			file_stat = regs.cx;
Z			fprintf(stderr,"new attribute for %s = %x\n",
Z				current,file_stat);
Z			break;
Z		}
Z	}
Z}
Z
STUNKYFLUFF
set `sum chmod.c`
if test 37605 != $1
then
echo chmod.c: Checksum error. Is: $1, should be: 37605.
fi
#
#
echo Extracting cmdlist.c:
sed 's/^Z//' >cmdlist.c <<\STUNKYFLUFF
Z
Zextern int cmds(),ls(), cp(), rm(), do_prog(),pushd(),popd(),drive(), ver(),
Z		more(),fgrep(),scr_clear(),set(),ch_mod(),cat(),echo(), 
Z		y(),t(),dump(),
Z		last(),invalid(),mv(),md(),touch(),cd(),pwd(),rd(),hist(),my_exit();
Ztypedef struct
Z{
Z	char *cmdname;
Z	int (*func)();
Z} builtin;
Zbuiltin commands[] =
Z{
Z	"a:",drive,
Z	"b:",drive,
Z	"c:",drive,
Z	"cat",cat,
Z	"cd",cd,
Z	"chdir",cd,
Z	"chmod",ch_mod,
Z	"cls",scr_clear,
Z	"commands",cmds,
Z	"copy",cp,
Z	"cp",cp,
Z	"copy",cp,
Z	"d:",drive,
Z	"del",rm,
Z	"dir",ls,
Z	"dump",dump,
Z	"e:",drive,
Z	"echo",echo,
Z	"era",rm,
Z	"erase",rm,
Z	"error",last,
Z	"exit",my_exit,
Z	"f:",drive,
Z	"fgrep",fgrep,
Z	"g:",drive,
Z	"h:",drive,
Z	"hd",dump,
Z	"hist",hist,
Z	"history",hist,
Z	"i:",drive,
Z	"j:",drive,
Z	"ls",ls,
Z	"md",md,
Z	"mkdir",md,
Z	"more",more,
Z	"mv",mv,
Z	"no history",invalid,
Z	"popd",popd,
Z	"pushd",pushd,
Z	"pwd",pwd,
Z	"rd",rd,
Z	"rm",rm,
Z	"rmdir",rd,
Z	"set",set,
Z	"tee",t,
Z	"touch",touch,
Z	"version",ver,
Z	"y",y
Z};
Zint numcmds =  (sizeof(commands)/sizeof(builtin));
STUNKYFLUFF
set `sum cmdlist.c`
if test 20023 != $1
then
echo cmdlist.c: Checksum error. Is: $1, should be: 20023.
fi
#
#
echo Extracting cmds:
sed 's/^Z//' >cmds <<\STUNKYFLUFF
Za:              b:              c:              cat             
Zcd              chdir           chmod           cls             
Zcommands        copy            cp              copy            
Zd:              del             dir             dump            
Ze:              echo            era             erase           
Zerror           exit            f:              fgrep           
Zg:              h:              hd              hist            
Zhistory         i:              j:              ls              
Zmd              mkdir           more            mv              
Zno history      popd            pushd           pwd             
Zrd              rm              rmdir           set             
Ztee             touch           version         y               
Z
STUNKYFLUFF
set `sum cmds`
if test 18223 != $1
then
echo cmds: Checksum error. Is: $1, should be: 18223.
fi
#
#
echo Extracting cmds.c:
sed 's/^Z//' >cmds.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <fcntl.h>
Ztypedef struct
Z{
Z	char *cmdname;
Z	int (*func)();
Z} builtin;
Z
Zchar *str_lower();
Z
Zextern int result;
Z
Zextern int cmds(),ls(), cp(), rm(), do_prog(),pushd(),popd(),drive(), ver(),
Z		more(),fgrep(),scr_clear(),set(),ch_mod(),cat(),echo(), 
Z		y(),t(),dump(),
Z		last(),invalid(),mv(),md(),touch(),cd(),pwd(),rd(),hist(),my_exit();
Z
Zmy_exit(argc,argv)
Z	char *argv[];
Z{
Z	exit(result);
Z}
Z
Zver()
Z{
Z	extern char *version;
Z	write(2,version,strlen(version));
Z	write(2,"\r\n",2);
Z}
Z
Zextern builtin commands[];
Zextern int numcmds;
Zchar *histerr = "no history";
Zint j,hiscount;
Zchar *history[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
Z					 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
Z
Zint histsize = (sizeof(history)/sizeof(char *));
Z
Zcmds()
Z{
Z	char *current;
Z	register int i,j,col;
Z	col = 1;
Z	for (i = 0; i < numcmds; i++)
Z	{
Z		current = commands[i].cmdname;
Z		write(1,current,j = strlen(current));
Z		for (;j < 16;j++)
Z			write(1," ",1);
Z		if (col == 4)
Z		{
Z			col = 1;
Z			crlf();
Z		}
Z		else
Z			++col;
Z	}
Z	crlf();
Z}
Z
Zfindcmd(cmdbuf)
Z	char *cmdbuf;
Z{
Z	register int low,high,mid;
Z	char localbuf[256];
Z	int cond;
Z	strcpy(localbuf,cmdbuf);
Z	cmdbuf = str_lower(localbuf);
Z	low = 0;
Z	high = numcmds - 1;
Z	while (low <= high)
Z	{
Z		mid = (low+high) / 2;
Z		if	( ( cond =  strncmp( cmdbuf,
Z								 commands[mid].cmdname,
Z								 strlen(commands[mid].cmdname) ) ) < 0 )
Z				high = mid - 1;
Z		else if (cond > 0)
Z				low = mid + 1;
Z		else
Z		{
Z			/* kludge to allow for program invocations like d:command */
Z			if (cmdbuf[1] == ':')
Z				if (cmdbuf[2] == '\0')
Z					return mid;
Z				else
Z					return -1;
Z			return mid;
Z		}
Z	}
Z	return -1;
Z}
Z
Zhist()
Z{
Z	register int i;
Z	char localbuf[256];
Z	if (j < histsize)
Z		i = 0;
Z	else
Z		i = j - histsize + 1;
Z	for (;i <= j; i++)
Z	{
Z		sprintf(localbuf,"%d : %s\r\n",i,history[i % histsize]);
Z		write(1,localbuf,strlen(localbuf));
Z	}
Z}
Z
Zlast()
Z{
Z	printf("return code of last command %d\n",result);
Z	return result;
Z}
STUNKYFLUFF
set `sum cmds.c`
if test 01085 != $1
then
echo cmds.c: Checksum error. Is: $1, should be: 01085.
fi
#
#
echo Extracting cp.c:
sed 's/^Z//' >cp.c <<\STUNKYFLUFF
Z#include <stdio.h>
Zchar *me;
Z/* cp.c - implements a version of UNIX cp */
Zchar target_name[128];
Z#ifndef MAIN
Zcp
Z#else
Zmain
Z#endif
Z(argc,argv)
Zint argc;
Zregister char *argv[];
Z{
Z	static char *usage = "cp : usage cp file1 [file2 . . fileN] target\r\n";
Z    char target[128],*fname_part();
Z    register int i;
Z	me = argv[0];
Z    if (argc < 3)
Z    {
Z	write(2,usage,strlen(usage));
Z	return(-1);
Z    }
Z    strcpy(target, argv[argc-1]);
Z    /* kill trailing backslashes */
Z    if (target[i = strlen(target) - 1] == '\\')
Z	target[i] = '\0';
Z    if (argc == 3)
Z    {
Z	if (target[1] == ':' && !target[2])
Z	    strcat(target,fname_part(argv[1]));
Z	/* if the target doesn't exist and it's not a directory then rename */
Z	if (access(target,0) && !dirp(target))
Z	{
Z	    fprintf(stderr,"copying %s to %s\n",argv[1],target);
Z	    filecopy(target,argv[1]);
Z	}
Z	else
Z	{
Z	    /* if the target is a directory copy to same name that directory */
Z	    if (dirp(target))
Z	    {
Z		int len;
Z		strcpy(target_name,target);
Z		if (target_name[(len = strlen(target_name))-1] != '\\')
Z		{
Z		    target_name[len = strlen(target_name)] = '\\';
Z		    target_name[len+1] = '\0';
Z		}
Z		strcat(target_name,fname_part(argv[1]));
Z		fprintf(stderr,"copying %s to %s\n",argv[1],target_name);
Z		filecopy(target_name,argv[1]);
Z	    }
Z	    else
Z	    {
Z		fprintf(stderr,"copying %s to %s\n",argv[1],target);
Z		filecopy(target,argv[1]);
Z	    }
Z	}
Z	return(0);
Z    }
Z    /* handle special case of a drive designation */
Z    if (target[(i = strlen(target))-1] != ':')
Z	if (!dirp(target))
Z	{
Z	    fprintf(stderr,"cp : %s isn't a directory\n",target);
Z	    return(-1);
Z	}
Z    for (i = 1; i < argc-1; i++)
Z    {
Z	int len;
Z	strcpy(target_name,target);
Z	if (target_name[(len = strlen(target_name))-1] != '\\')
Z	{
Z	    target_name[len = strlen(target_name)] = '\\';
Z	    target_name[len+1] = '\0';
Z	}
Z	strcat(target_name,fname_part(argv[i]));
Z	if (!filep(argv[i]))
Z	{
Z	    fprintf(stderr,"cp : %s isn't a file\n",argv[i]);
Z	    continue;
Z	}
Z	fprintf(stderr,"copying %s to %s\n",argv[i],target_name);
Z	filecopy(target_name,argv[i]);
Z    }
Z	return 0;
Z}
STUNKYFLUFF
set `sum cp.c`
if test 27151 != $1
then
echo cp.c: Checksum error. Is: $1, should be: 27151.
fi
#
#
echo Extracting crlf.c:
sed 's/^Z//' >crlf.c <<\STUNKYFLUFF
Zcrlf()
Z{
Z	static char *cr = "\r\n";
Z	write(1,cr,2);
Z}
STUNKYFLUFF
set `sum crlf.c`
if test 19813 != $1
then
echo crlf.c: Checksum error. Is: $1, should be: 19813.
fi
#
#
echo Extracting croot.c:
sed 's/^Z//' >croot.c <<\STUNKYFLUFF
Z/* Copyright (C) 1981,1982, 1983 by Manx Software Systems */
Z#include <errno.h>
Z#include <fcntl.h>
Z#ifndef NULL
Z#define NULL ((void *)0)
Z#endif
Z
Zchar *get_first(), *get_next(), *sbrk();
Z
Z#define ARGMAX 256
Zstatic char *Argv[ARGMAX];
Zstatic int argvsize;
Zstatic int Argc;
Zstatic char curr_path[128];
Z
Znoper()
Z{
Z	return 0;
Z}
Z
Zint (*cls_)() = noper;
Zextern char _ioflg[];
Z
ZCroot(cp, first)
Zregister char *cp;
Z{
Z	register char *cp2;
Z	char *save;
Z	char *wild_match;
Z	char *index(),*rindex(), *save_str();
Z	char *path,*copy; int j;
Z
Z	_ioflg[0] = isatty(0);	/* set flag for i/o routines */
Z	_ioflg[1] = isatty(1);	/* set flag for i/o routines */
Z	_ioflg[2] = isatty(2);	/* set flag for i/o routines */
Z
Z
Z	/* Null out first argument */
Z	Argv[0] = "";
Z	Argc = first;
Z
Z	/* loop through arguments */
Z	for (;;) 
Z	{
Z		/* skip blanks */
Z		while (*cp == ' ' || *cp == '\t')
Z			++cp;
Z
Z		/* if you're at the end of command line, you're done */
Z		if (*cp == 0)
Z			break;
Z
Z		/* find beginning of next argument */
Z		cp2 = cp;	/* save original pointer to the string */
Z		*cp2 = (*cp2 == '/' ? '\\' : *cp2);
Z		while (*++cp2)
Z		{
Z			/* if you hit a space char - stick a null in to terminate last
Z			   argument
Z			 */
Z			if (*cp2 == ' ' || *cp2 == '\t') 
Z			{
Z				*cp2++ = 0;
Z				break;
Z			}
Z			*cp2 = (*cp2 == '/' ? '\\' : *cp2);
Z		}
Z
Z		/* if no wild card characters, do it the old fashioned way */
Z		if (index(cp,'*') == NULL && index(cp,'?') == NULL)
Z		{
Z			/* update the next argv pointer */
Z			Argv[Argc] = cp;
Z			/* bump the argument count */
Z			if (++Argc == ARGMAX)
Z				abort();
Z		}
Z		else
Z		{
Z			/* if there is a path included, save it off */
Z			if ((path = rindex(cp,'\\')) || (path = rindex(cp,'/')))
Z			{
Z				copy = cp;
Z				/* copy to curr_path, mapping / to \ */
Z				for (j = 0; j < sizeof(curr_path) && copy != path+1; copy++,j++)
Z					curr_path[j] = (*copy == '/' ? '\\' : *copy);
Z				/* terminate string */
Z				curr_path[j] = '\0';
Z			}
Z			else if (cp[1] == ':')
Z			{
Z				copy = cp;
Z				for (j = 0; j < 2; j++)
Z					curr_path[j] = *copy++;
Z				curr_path[j] = '\0';
Z			} else
Z			/* null path */
Z				curr_path[0] = 0;
Z			if (wild_match = get_first(cp))
Z			{
Z				/* update the next argv pointer */
Z				Argv[Argc]= save_str(wild_match);
Z				/* bump the argument count */
Z				if (++Argc == ARGMAX)
Z					abort();
Z				/* get the rest of the matching file names */
Z				while (wild_match = get_next())
Z				{
Z					
Z					/* update the next argv pointer */
Z					Argv[Argc] = save_str(wild_match);
Z					/* bump the argument count */
Z					if (++Argc == ARGMAX)
Z						abort();
Z				}
Z			}
Z		}
Z		cp = cp2;	/* point to beginning of next argument */
Z	}
Z	Argv[Argc] = NULL;	
Z	main(Argc,Argv);
Z	exit(0);
Z}
Z
Zchar *save_str(s)
Z	register char *s;
Z{
Z	register char *r;
Z	int pathlen;
Z	/* squirrel away matched file name */
Z	if (NULL == (r = sbrk(strlen(s)+(pathlen = strlen(curr_path))+1)))
Z		abort();
Z	strcat(curr_path,s);
Z	strcpy(r,curr_path);
Z	curr_path[pathlen] = '\0';
Z	return r;
Z}
Z
Zabort()
Z{
Z	write(2, "Too many args.", 14);
Z	_exit(200);
Z}
Z
Zexit(code)
Z{
Z	(*cls_)();
Z	_exit(code);
Z}
Z
Ztypedef struct
Z{
Z	char dos_reserved[21];
Z	char attribute;
Z	unsigned file_time;
Z	unsigned file_date;
Z	long file_size;
Z	char file_name[13];
Z} fcb;
Zfcb wildcard;
Z
Zchar *get_first(fname)
Z	char *fname;
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&wildcard);
Z	result = bdos(0x4E,fname,0);
Z	/* make the find first call */
Z	if(2 == result || 18 == result)
Z		return NULL;
Z	return &(wildcard.file_name[0]);
Z}
Z
Zchar *get_next(fname)
Z	char *fname;
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&wildcard);
Z	result = bdos(0x4f,0,0);
Z	/* make the find next call */
Z	if (18 == result)
Z		return NULL;
Z	return &(wildcard.file_name[0]);
Z}
STUNKYFLUFF
set `sum croot.c`
if test 02895 != $1
then
echo croot.c: Checksum error. Is: $1, should be: 02895.
fi
#
#
echo Extracting ctlbrk.asm:
sed 's/^Z//' >ctlbrk.asm <<\STUNKYFLUFF
Z; Copyright (C) 1985 by Manx Software Systems, Inc.
Z; :ts=8
Z	include lmacros.h
Z
Zdataseg	segment	word public 'data'
Z	extrn	_PSP_:word
Z	_brkvec	dw ?
Z		dw ?
Z	localstk	dw	20	dup(?)
Z	stktop	label	word
Z	savess	dw ?
Z	savesp	dw ?
Zdataseg	ends
Z
Zourds	dw	0
Z
Z	assume	ds:dataseg
Z;
Z	procdef	ctl_brk_setup
Z	mov	ourds,ds
Z	push	ds
Z	mov	ax,3523H	;get cntl-break (cntl-c) handler
Z	int	21H
Z	mov	_brkvec,bx
Z	mov	_brkvec+2,es
Z	mov	dx,offset brk_handler
Z	mov	ax,cs
Z	mov	ds,ax
Z	mov	ax,2523H	;set new cntl-break handler
Z	int	21H
Z	pop	ds
Z	pret
Z	pend	ctl_brk_setup
Z
Z	procdef	ctl_brk_restore
Z	push	ds
Z	mov	dx,_brkvec
Z	mov	bx,word ptr _brkvec+2
Z	mov	ds,bx
Z	mov	ax,2523H	;restore old cntl-break handler
Z	int	21H
Z	pop	ds
Z	pret
Z	pend	ctl_brk_restore
Z
Zbrk_handler proc far
Z	;save ds and address our data segment
Z	push	ds
Z	mov	ds,ourds
Z	;move to the local stack after saving dos stack
Z	push	ax
Z	mov	savess,ss
Z	mov	savesp,sp
Z	mov	ax,ds
Z	mov	ss,ax
Z	mov	sp,offset stktop
Z	;save registers
Z	push	bp
Z	push	bx
Z	push	cx
Z	push	dx
Z	push	si
Z	push	di
Z	push	es
Z	mov	ah,051H	;find the current psp
Z	int	21H
Z	cmp	bx,_PSP_	;is it our program segment?
Z	je	noabort
Z	;set carry flag
Z	mov	ax,0FFFFH	;set up to shift bit into carry
Z	rcr	ax,1
Z	jmp	short done
Znoabort:
Z	;clear carry flag
Z	or	ax,ax		;should clear carry
Zdone:
Z	pop	es
Z	pop	di
Z	pop	si
Z	pop	dx
Z	pop	cx
Z	pop	bx
Z	pop	bp
Z	mov	ax,savess
Z	mov	ss,ax
Z	mov	sp,savesp
Z	pop	ax
Z	pop	ds
Z	jc	abortend
Z	iret
Zabortend:
Z	ret
Zbrk_handler endp
Z	finish
Z	end
STUNKYFLUFF
set `sum ctlbrk.asm`
if test 52105 != $1
then
echo ctlbrk.asm: Checksum error. Is: $1, should be: 52105.
fi
#
#
echo Extracting doprog.c:
sed 's/^Z//' >doprog.c <<\STUNKYFLUFF
Zdo_prog(argc,argv)
Zchar *argv[];
Z{
Z	int result;
Z	if (666 == (result = fexecvp(argv[0],argv)))
Z	{
Z		invalid(argc,argv);
Z		perror("");
Z		return -1;
Z	}
Z	return result; 
Z}
STUNKYFLUFF
set `sum doprog.c`
if test 44858 != $1
then
echo doprog.c: Checksum error. Is: $1, should be: 44858.
fi
#
#
echo Extracting drive.c:
sed 's/^Z//' >drive.c <<\STUNKYFLUFF
Z#define NULL (void *)0
Zdrive(argc,argv)
Z	char *argv[];
Z{
Z	char *dir,*getcwd();
Z
Z	bdos(0xe,**argv - 'a');	/* select drive 0 */
Z	if (NULL == (dir = getcwd(NULL,64)))
Z		return -1;
Z	free(dir);
Z	return 0;
Z}
STUNKYFLUFF
set `sum drive.c`
if test 37068 != $1
then
echo drive.c: Checksum error. Is: $1, should be: 37068.
fi
#
#
echo Extracting dump2.c:
sed 's/^Z//' >dump2.c <<\STUNKYFLUFF
Z
Z/*  dump.c  (10/83)  Debug style dump of a file from any starting position.
Z*/
Z#include <stdio.h>
Z#include <ctype.h>
Z#include <signal.h>
Z#include <setjmp.h>
Z#define FAIL        1
Z#define SUCCESS     0
Z#define TRUE      	(1)
Z#define FALSE       0
Z#define FOREVER     for (;;)
Z#define PAUSE       if (getch()=='\0') getch();
Z#define STDIN       0
Z#define STDOUT      1
Z#define STDERR      2
Zchar *dumpusage[] =
Z{
Z  "Usage: dump filespec [block [page]] | [segment:[offset]] [count]\r\n",
Z  "Where a block is 64K bytes and a page is 256 bytes.\r\n",
Z  "Segment:offset are standard 8086 notation in hexadecimal.\r\n",
Z  "Count is the number of bytes to dump in decimal.\r\n",
Z  NULL
Z};
Zchar *index();		/* suppress warning about conversion to int */
Z#define BUFSIZE 512
Ztypedef unsigned    ushort;
Z
Zstatic int _fmode = 0x8000;            /* Lattice 'c' - forces binary I/O */
Zextern short errno;             /* DOS 2.0 error number */
Zlong lseek();
Z
Zstatic char   buffer[BUFSIZE];         /* input buffer */
Zstatic ushort block = 0;               /* block number ( 64k bytes/block ) */
Zstatic ushort page = 0;                /* page number ( 256 bytes/page ) */
Zstatic ushort segment = 0;
Zstatic ushort offset = 0;
Zstatic long   filpos = 0;              /* beginning file position */
Zstatic long   count = 0x7FFFFFFFL;     /* number of bytes to dump */
Z
Zvoid ohw(),ohb(),onib(),abort();
Z
Zstatic jmp_buf env;
Zvoid (*signal())();
Zstatic void (*oldsig)();
Zstatic onintr()
Z{
Z	signal(SIGINT,SIG_IGN);	/* disable interrupts from kbd */
Z	longjmp(env,-1);
Z}
Z
Z#ifdef MAIN
Zmain
Z#else
Zdump
Z#endif
Z(argc,argv)                 /* DUMP ENTRY */
Zint  argc;
Zchar *argv[];
Z{
Z   char c;
Z   ushort i, numin, tot, file, cfrom;
Z   char *flag = 0;
Z   char *index();
Z   oldsig=signal(SIGINT,onintr);
Z   if (-1 == setjmp(env))
Z   {
Z   		close(file);
Z		write(2,"Interrupted\r\n",13);
Z		signal(SIGINT,oldsig);
Z		return -1;
Z   }
Z   if (argc < 2)
Z   {
Z   		char **u = (char **) dumpusage;
Z		while(*u)
Z		{
Z			write (2,*u,strlen(*u));
Z			++u;
Z		}
Z		return -1;
Z   }
Z   if ((file = open( argv[1], 0 )) == -1)
Z      abort( "cannot open", argv[1], errno );
Z
Z   if (argc > 2) {
Z      if ((flag = index( argv[2], ':' )) != NULL) {
Z         i = stch_i( argv[2], &segment );
Z         stch_i( argv[2]+i+1, &offset );
Z      }
Z      if (sscanf( argv[2], "%d", &block ) != 1)
Z         abort( "invalid block", argv[2], 0 );
Z   }
Z   if (argc > 3)
Z      if (sscanf( argv[3], "%d", &page ) != 1)
Z         abort( "invalid page", argv[3], 0 );
Z
Z   if ( flag ) {
Z      filpos = (long)segment*16L + (long)offset;
Z      tot = offset;
Z   }
Z   else {
Z      filpos = (block * 65536L) + (page * 256);
Z      tot = page * 256;
Z      segment = block * 4096;
Z   }
Z
Z   if (lseek( file, filpos, 0 ) == -1L)
Z      abort( "positioning to", argv[2], errno );
Z
Z   if (argc > 4)
Z      if (sscanf( argv[4], "%ld", &count ) != 1)
Z         abort( "invalid count", argv[4], 0 );
Z
Z
Z   do {                                    /* read & dump BUFSIZE bytes */
Z      numin = read( file, buffer, BUFSIZE );
Z      if (numin == -1)
Z         abort( "cannot read", argv[1], errno );
Z      cfrom=0;
Z      while (cfrom < numin) {
Z
Z         ohw(segment);                     /* print offset in hex */
Z         putchar(':');
Z         ohw(cfrom+tot);
Z         putchar(' ');
Z
Z         for (i=0; i < 16; i++) {          /* print 16 bytes in hex */
Z            putchar(' ');
Z            ohb(buffer[cfrom++]);
Z         }
Z
Z         putchar(' '); putchar(' '); putchar(' ');
Z
Z         cfrom -= 16;
Z         for (i=0; i < 16; i++) {          /* print 16 bytes in ASCII */
Z            c = buffer[cfrom] & 0x7f;
Z            if ( isprint(c) )              /* if printable character */
Z               putchar(c);
Z            else
Z               putchar('.');               /* else print period */
Z            cfrom++;
Z         }
Z
Z         putchar('\r'); putchar('\n');     /* print CR/LF */
Z
Z         if ((count -= 16) <= 0)             /* is count exhausted? */
Z            exit(0);
Z      }                                    /* end of while */
Z      tot += numin;
Z      if ( tot == 0 )
Z         segment += 4096;
Z   }                                       /* end of do */
Z   while (numin == BUFSIZE);
Z	signal(SIGINT,oldsig);					/* restore signal */
Z	return 0;
Z}                                          /* end of main */
Z
Zstatic void ohw(wrd)                        /*      print a word in hex     */
Zushort wrd;
Z{
Z   ohb( wrd>>8 );
Z   ohb( wrd );
Z}
Z
Zstatic void ohb(byt)                        /*      print a byte in hex     */
Zchar byt;
Z{
Z   onib( byt>>4 );
Z   onib( byt );
Z}
Z
Zstatic void onib(nib)            /*      print a nibble as a hex character   */
Zchar nib;
Z{
Z   nib &= 15;
Z   putchar((nib >= 10) ? nib-10+'A': nib+'0');
Z}
Z
Zstatic void abort( msg1 ,msg2 ,errno)     /*  print error msg1, msg2, and nbr */
Zchar *msg1,*msg2;                  /*   Does not close files.  */
Zshort errno;
Z{
Z   char stemp[10];
Z
Z   write( STDERR, "ERR: ", 5 );
Z   if (msg1)
Z      write( STDERR, msg1, strlen(msg1) );
Z   if (msg2)
Z      write( STDERR, " ", 1 );
Z      write( STDERR, msg2, strlen(msg2) );
Z   if (errno)   {
Z      sprintf( stemp," #%d", errno );
Z      write( STDERR, stemp, strlen(stemp) );
Z   }
Z   write( STDERR, "\r\n", 2 );
Z   longjmp(env,-1);
Z}
Z/** END DUMP **/
Z
Z#define BDOS_IN   7     /* input function for "getch" */
Z#define BDOS_OUT  6     /* output function for "putch" */
Z#define BDOS_CKS  11    /* check keyboard status for "kbhit" */
Z#define BDOS_BKI  10    /* buffered keyboardd input for "cgets" */
Z#define BDOS_PRT  9     /* print string for "cputs" */
Z
Zstatic char pushback;   /* character save for "ungetch" */
Z
Zstatic getch()
Z{
Zint c;
Z
Zif (pushback != '\0')
Z   {                    /* character was pushed back */
Z   c = pushback;
Z   pushback = '\0';
Z   return(c);
Z   }
Zreturn(bdos(BDOS_IN, 0xFF) & 127);
Z}
Zstatic putch(c)
Zchar c;
Z{
Zbdos(BDOS_OUT, c&127);
Zreturn(c);
Z}
Zstatic ungetch(c)
Zchar c;
Z{
Z
Zif (pushback != '\0') return(-1);
Zpushback = c;
Zreturn(c);
Z}
Zstatic char *cgets(s)
Zchar *s;
Z{
Zchar *p;
Z
Zif (*s == 0) *s = 250;          /* do not allow zero byte count */
Zbdos(BDOS_BKI, s);
Zp = s+2;
Zp[s[1]] = '\0';                 /* set terminating byte */
Zreturn(p);
Z}
Zstatic cputs(s)
Zchar *s;
Z{
Zchar *p;
Z
Zfor (p = s; *p != '\0'; p++) ;          /* find string terminator */
Z*p = '$';
Zbdos(BDOS_PRT, s);
Z*p = '\0';
Zreturn;
Z}
Z
Z
Zstatic int stch_i(p,r)
Z	char *p;
Z	int	*r;
Z{
Z	int count;
Z	int acc;
Z	int hdtoi();
Z	count = 0;
Z	*r = 0;
Z	while (-1 != (acc = hdtoi(*p++)))
Z	{
Z		++count;
Z		*r = (*r << 4) | acc;
Z	}
Z	return count;
Z}
Z
Zstatic hdtoi(c)
Z	char c;
Z{
Z	c = toupper(c);
Z	if (!isxdigit(c)) 
Z		return -1;
Z	if (isdigit(c))
Z		return (c - '0');
Z	return (c - 'A' + 10);
Z}
STUNKYFLUFF
set `sum dump2.c`
if test 53530 != $1
then
echo dump2.c: Checksum error. Is: $1, should be: 53530.
fi
#
#
echo Extracting echo.c:
sed 's/^Z//' >echo.c <<\STUNKYFLUFF
Z
Zecho(argc,argv)
Z	char *argv[];
Z{
Z	register int i;
Z	for (i = 1; i < argc;i++)
Z	{
Z		write(2,argv[i],strlen(argv[i]));
Z		if (i < argc-1)
Z			write(2," ",1);
Z	}
Z	crlf();
Z	return 0;
Z}
STUNKYFLUFF
set `sum echo.c`
if test 29675 != $1
then
echo echo.c: Checksum error. Is: $1, should be: 29675.
fi
#
#
echo Extracting env.c:
sed 's/^Z//' >env.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z
Z#define ENVSIZE 4000
Zextern int _PSP;
Zvoid *calloc();
Zchar *environment=NULL, *str_upper();
Zint env_paragraph;
Zchar *next_env;
Z#define envlimit(a) &a[ENVSIZE-1]
Z
Z#ifdef MAIN
Zmain
Z#else
Zset
Z#endif
Z(argc,argv)
Z	char *argv[];
Z{
Z	if (!environment)
Z		init_env();
Z	if (argc == 1)
Z	{
Z		show_env();
Z		return 0;
Z	}
Z	while(--argc)
Z	{
Z		add_env(str_upper(*(++argv)));
Z	}
Z	return 0;
Z}
Z
Zchar *
Zstr_upper(c)
Z	register char *c;
Z{
Z	register char *save = c;
Z	while(*c)
Z	{
Z		*c = toupper(*c);
Z		c++;
Z	}
Z	return save;
Z}
Z
Zchar *
Zstr_lower(c)
Z	register char *c;
Z{
Z	register char *save = c;
Z	while(*c)
Z	{
Z		*c = tolower(*c);
Z		c++;
Z	}
Z	return save;
Z}
Zinit_env()
Z{
Z	extern unsigned _dsval;	/* current data segment register value */
Z	long fudgefactor;
Z	register int c;
Z	unsigned envseg;
Z	unsigned offset = 0;
Z	envseg = peekw(0x2c,_PSP);
Z	environment = calloc(1,ENVSIZE+16);
Z	fudgefactor = (long)_dsval << 4;	/* convert to absolute paragraph */
Z	fudgefactor += (unsigned)environment + 16;
Z	fudgefactor &= 0xFFFF0L;
Z	env_paragraph = (int)((fudgefactor >>4) & 0xFFFF);
Z	environment = (char *) (fudgefactor - (long)(_dsval << 4));
Z	next_env = environment;
Z	while (c = peekb(offset,envseg))
Z	{
Z		while (c = peekb(offset++,envseg))
Z		{
Z			*next_env++ = c;
Z		}
Z		*next_env++ = '\0';
Z	}
Z}
Z
Zshow_env()
Z{
Z	register char *env;
Z	char c;
Z	for (env = environment;*env;)
Z	{
Z		while (c = *env++)
Z			write(1,&c,1);
Z		crlf();
Z	}
Z}
Z
Zstatic char *enverr = "No more environment space\r\n";
Zstatic char *enverr2 = "Improper environment string format!!\r\n";
Z
Zadd_env(string)
Z	char *string;
Z
Z{
Z	char *env_copy, *new, *index();
Z	char *old = environment;
Z	char *name_end,*new_name_end;
Z	int added = 0;
Z	int namelen;
Z
Z	if (NULL == (env_copy = new = calloc(1,ENVSIZE)))
Z	{
Z		write(2,enverr,strlen(enverr));
Z		return -1;
Z	}
Z
Z	while (*old)
Z	{
Z		if ( NULL == (name_end = index(old,'=')) || 
Z			NULL == (new_name_end = index(string,'=')) 
Z		)
Z		{
Z			write(2,enverr2,strlen(enverr2));
Z			free(env_copy);
Z			return -1;
Z		}
Z		namelen = (int)(name_end - old);
Z		if (!strncmp(old,string,namelen))
Z		{
Z			if (new_name_end[1])
Z			{
Z				/* if we don't have a string of the form name= */
Z				/* copy new string instead of old string */
Z				strcpy(new,string);
Z			}
Z			else
Z			/* if we have a set name= with no string then we want
Z			   to remove the string from the environment
Z			 */
Z				;
Z			added++;
Z		}
Z		else
Z		{
Z			strcpy(new,old);
Z		}
Z		new = &new[strlen(new)+1];
Z		old = &old[strlen(old)+1];
Z		if (new >= envlimit(new))
Z		{
Z			write(2,enverr,strlen(enverr));
Z			free(env_copy);
Z			return -1;
Z		}
Z	}
Z	if (!added)
Z	{
Z		strcpy(new,string);
Z	}
Z	new = &new[strlen(new)+1];
Z	/* copy the copy back to the environment */
Z	movmem(env_copy,environment,(int)(new-env_copy)+2);
Z	free(env_copy);
Z	return 0;
Z}
STUNKYFLUFF
set `sum env.c`
if test 56918 != $1
then
echo env.c: Checksum error. Is: $1, should be: 56918.
fi
#
#
echo Extracting fexec.asm:
sed 's/^Z//' >fexec.asm <<\STUNKYFLUFF
Z; Copyright (C) 1984 by Manx Software Systems
Z; :ts=8
Z	include lmacros.h
Zdataseg segment para public 'data'
Zparam	equ	this word
Zenv	dw	?
Zcline	dw	?,?
Zfcb1	dw	?,?
Zfcb2	dw	?,?
Z	extrn	errno_:word
Zdataseg	ends
Z	assume	ds:dataseg
Zsave_ss	dw	0
Zsave_sp	dw	0
Z	procdef	fexec,<<filname,ptr>,<enva,word>,<clinea,ptr>,<fcb1a,ptr>,<fcb2a,ptr>>
Z;			char *fexec(name,env,cline,fcb1,fcb2)
Z;
Z	push	si
Z	push	di
Z	pushf
Z	push	[030H]
Z	push	[02EH]
Z	push	ds
Z	push	es
Z	mov	cs:save_ss,ss
Z	mov	cs:save_sp,sp
Z;
Z;	set up parameter block for exec call
Z;
Z	mov	ax,enva
Z	mov	env,ax
Zifndef LONGPTR
Z	mov	ax,ds
Z	mov	es,ax
Zendif
Z	ldptr	ax,clinea,es
Z	mov	cline,ax
Z	mov	cline+2,es
Z	ldptr	ax,fcb1a,es
Z	mov	fcb1,ax
Z	mov	fcb1+2,es
Z	ldptr	ax,fcb2a,es
Z	mov	fcb2,ax
Z	mov	fcb2+2,es
Z;
Z	mov	ax,ds
Z	mov	es,ax
Z	mov	bx,offset param
Z	ldptr	dx,filname,ds		;name of file to exec
Z	mov	ax,04b00H
Z	int	21h
Z	mov	ss,cs:save_ss
Z	mov	sp,cs:save_sp
Z	pop	es
Z	pop	ds
Z	jnc	noerror
Z	mov	errno_,ax
Z	mov	ax,666
Z	jmp	short done
Znoerror:
Z	sub	ax,ax
Zdone:
Z	pop	[02EH]
Z	pop	[030H]
Z	popf
Z	pop	di
Z	pop	si
Z	pret
Z	pend	fexec
Z	finish
Z	end
STUNKYFLUFF
set `sum fexec.asm`
if test 35466 != $1
then
echo fexec.asm: Checksum error. Is: $1, should be: 35466.
fi
#
#
echo Extracting fexecv.c:
sed 's/^Z//' >fexecv.c <<\STUNKYFLUFF
Z#ifdef DEBUG
Z#ifndef FILE
Z#include <stdio.h>
Z#endif
Z#include <ctype.h>
Z#include <debug.h>
Z#endif
Z
Z
Z/* Copyright (C) 1983, 1984 by Manx Software Systems */
Z/* modified by kent williams to employ environment managed in env.c */
Zextern int env_paragraph;
Z
Zfexecv(path, argv)
Zchar *path, **argv;
Z{
Z	register char *cp, *xp;
Z	int i;
Z	char buffer[258];
Z	char fcb1[16], fcb2[16];
Z
Z	cp = buffer+1;
Z	i = 1;
Z	if (*argv) {
Z		++argv;			/* skip arg0, used for unix (tm) compatibility */
Z		while (xp = *argv++) {
Z			if (i == 1)
Z				fcbinit(xp, fcb1);
Z			else if (i == 2)
Z				fcbinit(xp, fcb2);
Z			while (*xp) {
Z				if (cp >= buffer+256)
Z					goto done;
Z				*cp++ = *xp++;
Z			}
Z			*cp++ = ' ';
Z			++i;
Z		}
Z	}
Zdone:
Z	buffer[0] = cp - (buffer+2);
Z	/* terminate string */
Z	buffer[buffer[0]+1] = 0;
Z#ifdef DEBUG
Z		fprintf(stderr,"\nbuffer[0] = %d\n",buffer[0]);
Z		for (i = 1; buffer[i] ; i++)
Z		{
Z			if (isprint(buffer[i]))
Z				putchar(buffer[i]);
Z			else
Z				fprintf("buffer[%d] = %d\n",i,buffer[i]);
Z		}
Z		crlf();
Z#endif
Z	return fexec(path, env_paragraph, buffer, fcb1, fcb2);
Z}
Z
STUNKYFLUFF
set `sum fexecv.c`
if test 22589 != $1
then
echo fexecv.c: Checksum error. Is: $1, should be: 22589.
fi
#
#
echo Extracting fexecvp.c:
sed 's/^Z//' >fexecvp.c <<\STUNKYFLUFF
Zfexecvp(name, argv)
Zchar *name, **argv;
Z{
Z	register char *cp, *xp;
Z	int result;
Z	char *getenv(), path[64];
Z
Z	if (666 != (result = tryexec("", name, argv)))
Z		return result;
Z	if ((cp = getenv("PATH")) != 0) {
Z		while (*cp) {
Z			xp = path;
Z			while (*cp) {
Z				if (*cp == ';') {
Z					++cp;
Z					break;
Z				}
Z				*xp++ = *cp++;
Z			}
Z			*xp = 0;
Z			if (path[0] != 0)
Z				if (666 != (result = tryexec(path, name, argv)))
Z					return result;
Z		}
Z	}
Z	return 666;
Z}
Z
Zstatic
Ztryexec(dir, name, argv)
Zchar *dir, *name, **argv;
Z{
Z	char newname[64];
Z	register char *cp;
Z	char *rindex(),*index();
Z
Z	strcpy(newname, dir);
Z	if (((cp = index(newname, '/')) || (cp = index(newname, '\\')))
Z				&& *(cp+1) != '\0')
Z		strcat(newname, "/");
Z	strcat(newname, name);
Z	if (index(name, '.') == 0) {
Z		strcat(newname, ".com");
Z		if (666 != fexecv(newname, argv))
Z			return wait();
Z		strcpy(rindex(newname,'.'), ".exe");
Z	}
Z	if (666 != fexecv(newname, argv))
Z			return wait();
Z	return 666;
Z}
STUNKYFLUFF
set `sum fexecvp.c`
if test 44134 != $1
then
echo fexecvp.c: Checksum error. Is: $1, should be: 44134.
fi
#
#
echo Extracting fgrep.c:
sed 's/^Z//' >fgrep.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <fcntl.h>
Z
Z#include <signal.h>
Z#include <setjmp.h>
Z
Zvoid (*signal())();
Zvoid (*fgrepsig)();
Zjmp_buf fgrep_env;
Z
Zvoid fgrep_intr()
Z{
Z	/* restore old signal */
Z	signal(SIGINT,fgrepsig);
Z	/* jump to exit */
Z	longjmp(fgrep_env,-1);
Z}
Z
ZFILE *fopen(),*fdopen();
Z
Zchar *fgets();
Z
Zfgrep( argc, argv )
Zint	argc;
Zchar * argv[];
Z	{
Z	FILE *  fd;
Z	int	rc;
Z	char * pattern;
Z	/* handle interrupts */
Z	if (-1 == setjmp(fgrep_env))
Z	{
Z		static char *intmsg = "Interrupted\r\n";
Z		write(2,intmsg,strlen(intmsg));
Z		fclose(fd);
Z		return -1;
Z	}
Z	/* set signal catcher */
Z	fgrepsig= signal(SIGINT,fgrep_intr);
Z	while( --argc )
Z		{
Z		if( (*++argv)[0] == '-' )
Z			switch( (*argv)[1] )
Z				{
Z				default:
Z					fprintf( stderr, "invalid argument %s\n", *argv );
Z					return(1);
Z				}
Z		else
Z			break;
Z		}
Z
Z	if( argc == 0 )
Z		{
Z		fprintf( stderr, "usage: fgrep pattern [file ...]\n" );
Z		return(1);
Z		}
Z
Z	pattern = *argv++;
Z	argc--;
Z
Z	rc = 0;
Z	if( argc == 0 )
Z		{
Z		fd = fdopen(0,"r");
Z		rc = _fgrep( NULL, pattern, fd);
Z		fclose(fd);
Z		return( 0 );
Z		}
Z	else
Z	while( argc-- )
Z		{
Z
Z		if( (fd = fopen( *argv, "r" )) == NULL )
Z			fprintf( stderr, "couldn't open %s\n", *argv );
Z		else
Z			{
Z			rc |= _fgrep( *argv, pattern, fd );
Z			fclose( fd );
Z			}
Z		argv++;
Z
Z		}
Z
Z	return( !rc );
Z	signal(SIGINT,fgrepsig);
Z	}
Z
Z
Z_fgrep( file, pattern, fd )
Zchar	* file;
Zchar	* pattern;
ZFILE *	fd;
Z	{
Z	char	line[BUFSIZ];
Z	int		rc;
Z	int		linenumber = 1;
Z	rc = 0;
Z	while( fgets( line, sizeof(line), fd ) != NULL )
Z	{
Z		if( rc = match( pattern, line ) )
Z			printf( "%s %d: %s", (file) ? file : "stdin", linenumber, line );
Z		linenumber++;
Z	}
Z	return( rc );
Z
Z	}
Z
Zmatch( pattern, line )
Zregister char	* pattern;
Zchar	* line;
Z	{
Z	/*  not a great algorithm  */
Z	register char * ptr;
Z	char	* end;
Z	int	plen = strlen(pattern);
Z	int llen = strlen(line);
Z
Z	if( plen > llen )
Z		return( 0 );
Z
Z	end = line+(llen-plen);
Z
Z	for( ptr=line; ptr < end; ptr++ )
Z		{
Z		if( strncmp( pattern, ptr, plen ) == 0 )
Z			return( 1 );
Z		}
Z
Z	return( 0 );
Z	}
STUNKYFLUFF
set `sum fgrep.c`
if test 15656 != $1
then
echo fgrep.c: Checksum error. Is: $1, should be: 15656.
fi
#
#
echo Extracting getcmd.c:
sed 's/^Z//' >getcmd.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <fcntl.h>
Z
Zchar *gets();
Zchar rest[256];
Zchar *rptr = NULL;
Zchar pipeactive = 0;
Zcurrname = 0;
Zchar *pipename[2] = {
Z	"shtmp1",
Z	"shtmp2"};
Zchar *
Zgetnextcmd(buf)
Z	char *buf;
Z{
Z	char *fgets(),*index(),*pipe,*semi;
Z	if (!rptr)
Z	{
Z		register char *c;
Z		if (0 == read(0,rest,sizeof(rest)))
Z			return NULL;
Z		c = rptr = rest;
Z		while (*c)
Z		{
Z			if (*c == '\r' || *c == '\n')
Z			{
Z				*c = '\0';
Z				break;
Z			}
Z			++c;
Z		}
Z
Z	}
Z	pipe = index(rptr,'|');
Z	semi = index(rptr,';');
Z	if (pipe == NULL && semi == NULL)
Z	{
Z		strcpy(buf,rptr);
Z		if (pipeactive)
Z		{
Z			pipeactive = 0;
Z			strcat(buf," < ");
Z			strcat(buf,pipename[currname]);
Z		}
Z		rptr=NULL;
Z	}
Z	/* one or the other, or both are not NULL, so comparison is in order */
Z	else if (pipe && (!semi || (pipe < semi)))
Z	{
Z		*pipe = '\0';	/* terminate string */
Z		strcpy(buf,rptr); /* copy to buf */
Z		rptr = pipe+1;	/* set up rest */
Z		if (pipeactive++)
Z		{
Z			pipeactive = 1;
Z			strcat(buf," < ");
Z			strcat(buf,pipename[currname]);
Z		}
Z		strcat(buf," > ");
Z		currname ^= 1;	/* flip flop pipe names */
Z		strcat(buf,pipename[currname]);
Z	}
Z	else if (semi && (!pipe || (semi < pipe)))
Z	/* we have a semicolon to deal with */
Z	{
Z		*semi = '\0';
Z		strcpy(buf,rptr);
Z		rptr = semi+1;
Z		if (pipeactive)
Z		{
Z			pipeactive = 0;
Z			strcat(buf," < ");
Z			strcat(buf,pipename[currname]);
Z		}
Z	}
Z	return buf;
Z}
STUNKYFLUFF
set `sum getcmd.c`
if test 06449 != $1
then
echo getcmd.c: Checksum error. Is: $1, should be: 06449.
fi
#
#
echo Extracting invalid.c:
sed 's/^Z//' >invalid.c <<\STUNKYFLUFF
Z
Zinvalid(argc,argv)
Z	char *argv[];
Z{
Z	register int i;
Z	static char *invmsg = "sh : bad command : ";
Z	write(2,invmsg,strlen(invmsg));
Z	for (i = 0; i < argc;i++)
Z	{
Z		write(2,argv[i],strlen(argv[i]));
Z		write(2," ",1);
Z	}
Z	return -1;
Z}
STUNKYFLUFF
set `sum invalid.c`
if test 00995 != $1
then
echo invalid.c: Checksum error. Is: $1, should be: 00995.
fi
#
#
echo Extracting ls.c:
sed 's/^Z//' >ls.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z
Ztypedef struct
Z{
Z	char attribute;
Z	unsigned file_time;
Z	unsigned file_date;
Z	long file_size;
Z	char file_name[13];
Z} file_desc;
Z
Ztypedef struct
Z{
Z	char dos_reserved[21];
Z	file_desc file;
Z} fcb;
Z
Z#define maxfiles 128 
Z
Zchar printbuf[256];
Zfile_desc *getfirst(),*getnext();
Zchar *index(), *rindex();
Zint mode = 0x10;
Zint verbose=0,column=4,recurse=0;
Zint quiet = 0;
Zint drivenum = 0;
Zlong time();
Zshort year;
Z
Zdo_return(result)
Z{
Z	verbose = quiet = recurse = 0;
Z	column = 4;
Z	return result;
Z}
Z#ifndef MAIN
Zls
Z#else
Zmain
Z#endif
Z(argc,argv)
Zchar *argv[];
Z{
Z	int noargs;
Z	char *current;
Z	char namebuf[128];
Z	/* 
Z	 * initialize statics
Z	 */
Z	mode = 0x10;
Z	verbose=0;column=4;recurse=0;
Z	quiet = 0;
Z	drivenum = 0;
Z	/*
Z	 * get current time
Z	 */
Z	year = (int)((time(NULL) >> 25) & 0x7F) + 80;
Z	/*
Z	 * set up a default search name
Z	 */
Z	if (noargs = (argc == 1))
Z		argc++;
Z	while(--argc)
Z	{
Z		if (noargs)
Z			current = "*.*";
Z		else
Z			current = *(++argv);	/* get current file name */
Z		if (*current == '-')
Z		{
Z			++current;	/* point past - */
Z			while (*current)
Z			{
Z				switch (*current++)
Z				{
Z				case 'l':
Z				case 'L':
Z					verbose = 1;
Z					if (column != 1)
Z						column = 2;
Z					if (quiet)
Z					{
Z						fprintf(stderr,"ls : verbose and quiet conflict\n");
Z						do_return(-1);
Z					}
Z					break;
Z				case 'q':
Z				case 'Q':
Z					quiet = 1;
Z					if (verbose)
Z					{
Z						fprintf(stderr,"ls : quiet and verbose conflict\n");
Z						do_return(-1);
Z					}
Z					break;
Z				case 'c':
Z				case 'C':
Z					column = 1;
Z					break;
Z				case 'a':
Z				case 'A':
Z					mode = 0x2 + 0x4 + 0x10;
Z					break;
Z				case 'r':
Z				case 'R':
Z					recurse = 1;
Z					mode = 0x2 + 0x4 + 0x10;
Z					break;
Z				default:
Z					break;
Z				}
Z			}
Z			/* if we're down to one argument after looking at all the
Z			   switches, we need to set noargs to true */
Z			if (noargs = (argc == 1))
Z				argc++;
Z			continue;
Z		}
Z		/* if a drive is specified, figure out what drive it is */
Z		if (current[1] == ':')
Z		{
Z			drivenum = toupper(current[0]) - 'A' + 1;
Z		}
Z		else
Z			drivenum = 0;
Z		/* if no wild cards, look for directory and drive names */
Z		if ( NULL == index(current,'?') && NULL == index(current,'*'))
Z		{
Z			if (getfirst(current)->attribute & 0x10)
Z			{
Z				strcpy(namebuf,current);
Z				strcat(namebuf,"\\*.*");
Z				current = namebuf;
Z			} 
Z			/* look for drive names */
Z			else if (current[strlen(current)-1] == ':' && 
Z						!current[strlen(current)])
Z			{
Z				strcpy(namebuf,current);
Z				strcat(namebuf,"\\*.*");
Z				current = namebuf;
Z			}
Z		}
Z		do_dir(current);
Z	}
Z	do_return( 0);
Z}
Z
Zdo_dir(current)
Z	char *current;
Z{
Z	typedef file_desc fblock[maxfiles];	/* as many as we'll likely need */
Z	file_desc *files;
Z	file_desc *curr_file,*getnext();
Z	void *malloc();
Z	unsigned int ftime,date;
Z	int i,j,col;
Z	int files_cmp();
Z	long total = 0;
Z	char atts[4]; /* drw */
Z	/* allocate file block */
Z	if (NULL == (files = malloc(sizeof(fblock))))
Z	{
Z		fprintf(stderr,"Not enough memory to do directory\n");
Z		return -1;
Z	}
Z	/* look for match */
Z	i = 0;
Z	if (!(curr_file = getfirst(current)))
Z	{
Z		printf(stderr,"ls : no files matching %s\n",current);
Z		free(files);
Z		return;	
Z	}
Z	files[i++] = *curr_file;
Z	/* get all matching */
Z	while ((curr_file = getnext()) && i < maxfiles)
Z		files[i++] = *curr_file;	
Z	if (i > 1)
Z		qsort(files,i,sizeof(file_desc),files_cmp);
Z	if (!quiet)
Z	{
Z		write(1,"\r\n",2);
Z		write(1,current,strlen(current));
Z		write(1,"\r\n",2);
Z	}
Z	col = 1;
Z	for (j = 0; j < i; j++)
Z	{
Z		register char *c = files[j].file_name;
Z		if (*c == '.')
Z			continue;	/* filter out . and .. */
Z		while (*c)
Z		{
Z			*c = tolower(*c);
Z			c++;
Z		}
Z		if (verbose)
Z		{
Z			register char att = files[j].attribute;
Z			register int fyear;
Z			fyear = ((files[j].file_date >> 9) & 0x7F)+80; 
Z			atts[3] = 0;	/* terminate string */
Z			atts[0] = att & 0x10 ? 'd' : '-';
Z			atts[1] = att & 2 ? '-' : 'r';
Z			atts[2] = att & 1 ? '-' : 'w';
Z			if (atts[0] == 'd')
Z			{
Z				register int k;
Z				sprintf(printbuf,"%s %s\\",atts,files[j].file_name);
Z				write(1,printbuf,strlen(printbuf));
Z				k = 12 - strlen(files[j].file_name)+7;
Z				while(k--)
Z					write(1," ",1);
Z
Z			}
Z			else
Z			{
Z				total += files[j].file_size;
Z				sprintf(printbuf,"%s %-12s %-6ld ",atts,files[j].file_name,
Z						files[j].file_size);
Z				write(1,printbuf,strlen(printbuf));
Z			}
Z			ftime = files[j].file_time;
Z			date = files[j].file_date;
Z			if (year == fyear)
Z			{
Z				sprintf(printbuf,"%02d/%02d %02d:%02d ",
Z					((date >> 5) & 0x0F),	/* month */
Z					date & 0x1F,		/* day	*/
Z					(ftime >> 11) & 0x1F,		/* hours */
Z					(ftime >> 5) & 0x3F);		/* minutes */
Z				write(1,printbuf,strlen(printbuf));
Z			}
Z			else
Z			{
Z				sprintf(printbuf,"%02d/%02d       ",
Z					((date >> 5) & 0x0F),	/* month */
Z					fyear,					/* file year */
Z					(ftime >> 11) & 0x1F,		/* hours */
Z					(ftime >> 5) & 0x3F);		/* minutes */
Z				write(1,printbuf,strlen(printbuf));
Z			}
Z		}
Z		else
Z		{
Z			if (files[j].attribute & 0x10)
Z			{
Z				register int k;
Z				sprintf(printbuf,"%s\\",files[j].file_name);
Z				write(1,printbuf,strlen(printbuf));
Z				k = 16 - strlen(files[j].file_name);
Z				while(--k)
Z					write(1," ",1);
Z
Z			}
Z			else
Z			{
Z				sprintf(printbuf,"%-13s   ",files[j].file_name);
Z				write(1,printbuf,strlen(printbuf));
Z			}
Z		}
Z		if (col == column)
Z		{
Z			col = 1;
Z			write(1,"\r\n",2);
Z		}
Z		else
Z			col++;
Z	}
Z	write(1,"\r\n",2);
Z	if (verbose)
Z	{
Z		sprintf(printbuf,"%ld bytes in %d files ",total,i);
Z		write(1,printbuf,strlen(printbuf));
Z		pr_freespace();
Z	}
Z	if (recurse)
Z		for (j = 0; j < i; j++)
Z		{
Z			/* we've got a subdirectory */
Z			if (files[j].attribute & 0x10 && files[j].file_name[0] != '.')
Z			{
Z				char *path;
Z				char dirname[48];
Z				if (!strcmp(current,"*.*"))
Z					dirname[0] = '\0';
Z				else
Z					strcpy(dirname,current);
Z				if (path = rindex(dirname,'\\'))
Z					*(++path) = '\0';
Z				strcat(dirname,files[j].file_name);	/* get name */
Z				strcat(dirname,"\\*.*");
Z				do_dir(dirname);
Z			}
Z		}
Z	free(files);
Z}
Z
Zfiles_cmp(a,b)
Z	file_desc *a,*b;
Z{
Z	return strcmp(a->file_name,b->file_name);
Z}
Z
Zfcb tmp;
Z
Zfile_desc *getfirst(fname)
Z	char *fname;
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&tmp);
Z	result = bdos(0x4E,fname,mode);
Z	/* make the find first call */
Z	if(2 == result || 18 == result)
Z		return NULL;
Z	return &(tmp.file);
Z}
Z
Zfile_desc *getnext()
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&tmp);
Z	result = bdos(0x4f,0,0);
Z	/* make the find next call */
Z	if (18 == result)
Z		return NULL;
Z	return &(tmp.file);
Z}
Z
Z/*	determine available space on default drive
Z*/
Z
Zpr_freespace()
Z{
Z	/* register arguments for INT */
Z    struct   {int ax,bx,cx,dx,si,di,ds,es;}sysr;
Z    unsigned int    retstat;		    /* flags returned from INT */
Z
Z#define 	    cls_avail  sysr.bx	    /* number of available clusters */
Z#define 	    cls_total  sysr.dx	    /* total number of clusters on volume */
Z#define 	    byt_sectr  sysr.cx	    /* bytes per sector */
Z#define 	    sec_clstr  sysr.ax	    /* sectors per cluster */
Z
Z    unsigned long   byt_clstr,		    /* bytes per cluster */
Z		    vol_total,		    /* total size of volume, bytes */
Z		    fre_total;		    /* size of free space, bytes */
Z
Z    sysr.ax = 0x3600;		    /* get disk free space function code */
Z    sysr.dx = drivenum;			    /* (DL) = drive id, 0=current, 1=A, */
Z    retstat = sysint(0x21,&sysr,&sysr);		    /* invoke DOS */
Z    if( sec_clstr!=0xffff ) 
Z	{
Z		byt_clstr = byt_sectr * sec_clstr;
Z		vol_total = cls_total * byt_clstr;
Z		fre_total = cls_avail * byt_clstr;
Z		fprintf(stdout,
Z		"%lu out of %lu bytes available on %c:\n", 
Z		fre_total,vol_total, 
Z		drivenum ? drivenum + 'A' - 1 : bdos(0x19,0,0)+'A');
Z	}
Z}
STUNKYFLUFF
set `sum ls.c`
if test 06358 != $1
then
echo ls.c: Checksum error. Is: $1, should be: 06358.
fi
#
#
echo Extracting main.c:
sed 's/^Z//' >main.c <<\STUNKYFLUFF
Z#line 1 "main.s"
Z#include <stdio.h>
Z#include <ctype.h>
Z#include <signal.h>
Z#include <fcntl.h>
Z#include <setjmp.h>
Ztypedef struct		/* used to find builtin commands */
Z{
Z	char *cmdname;
Z	int (*func)();
Z} builtin;
Z
Zextern builtin commands[];
Zextern char histerr[];
Zextern int j,hiscount;
Zextern char *history[];
Zextern int histsize;
Zextern int numcmds;
Zstatic int quiet = 0;
Z
Zchar *version = "SHELL VERSION 1.2 Kent Williams";
Z
Zjmp_buf env;
Z
Zchar *pipename[] =
Z{
Z	"\\shtmp1",
Z	"\\shtmp2"
Z};
Z
Zchar cmdbuf[512];
Zint  currname = 0;
Zint result = 0;
Z
Zmain(argc,argv)
Z	char *argv[];
Z{
Z	signal(SIGINT,SIG_IGN);	/* ignore breaks */
Z
Z	quiet = !isatty(0);		/* quiet = batch shell */
Z
Z	/* initialize local environment */
Z	init_env();
Z	cli();
Z	exit(0);
Z}
Z
Z#ifndef SNODEBUG
Z#define SNODEBUG
Z#endif
Z#line 46 "main.s"
Z
Z/*
Z * statemachine cli
Z */
Zcli () 
Z
Z#line 48 "main.s"
Z{
Z#line 49 "main.s"
Z/* global variables */
Z	int i;
Z	int repeat, state, inpipe = 0;
Z	static char localbuf[256];
Z	static char histbuf[256];
Z	static char tail[256];
Z	int histindex,argindex,takeline;
Z	char *local = localbuf;
Z	char *current,*curr_save;
Z	char *ntharg(), *argptr;
Z	char *savestr();
Z
Z
Z/*
Z * end of declarations for cli
Z */
Z/* $ */ goto getline;
Z#line 62 "main.s"
Z
Zgetline:
Z{
Z#line 62 "main.s"
Z		/* kill tmp files */
Z		unlink(pipename[0]); unlink(pipename[1]);
Z
Z		hiscount = j % histsize; /* hiscount is current position in history */
Z		if(!quiet)
Z			fprintf(stderr,"%d%% ",j);
Z
Z/*
Z * The following code simply reads a line from standard input.
Z * It is so complicated because when you save the standard stream
Z * files and execute another program/command, standard input is
Z * left in an uncertain state - the FILE stdin seems to be at EOF,
Z * even when standard input is associated with the console, and
Z * cr/lf combinations show up as line terminators, whereas usually
Z * only linefeeds get placed in the input stream.
Z * WHY? beats me.  Something could be wrong with
Z *  1. AZTEC C runtime
Z *  2. PCDOS
Z *  3. Me
Z *  4. All three, or permutations of 1-3 reducto ad absurdum.
Z * All I know is this works
Z */
Z		/* clear command buffer so string read is null terminated */
Z		setmem(cmdbuf,sizeof(cmdbuf),0);
Z		for (current = cmdbuf;;current++)
Z		{
Z			int readresult;
Z			if ((readresult = read(0,current,1)) == 0 ||
Z				readresult == -1)
Z			{
Z/* $ */ goto terminal;
Z			}
Z			if (*current == '\r')
Z			{
Z				if ((readresult = read(0,current,1)) == 0 ||
Z					readresult == -1)
Z				{
Z/* $ */ goto terminal;
Z				}
Z				*current = '\0';
Z				break;
Z			}
Z			else if (*current == '\n')
Z			{
Z				*current = '\0';	/* terminate string */
Z				break;
Z			}
Z		}
Z		current = cmdbuf;	/* point current at start of buffer */
Z/*
Z * end of input weirdness
Z */
Z		/* if we're recycling history strings, free previous one */
Z		if (history[hiscount])
Z			free(history[hiscount]);
Z
Z		/* save current in history array */
Z		history[hiscount] = savestr(current);
Z		/* parse command for compound statements and pipes */
Z		local = localbuf;	/* set pointer to state of buffer */
Z		setmem(localbuf,sizeof(localbuf),0);	/* clear buffer */
Z/* $ */ goto eatwhitespace;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate getline
Z */
Z};
Z#line 125 "main.s"
Z
Z
Zcharstate:
Z{
Z#line 127 "main.s"
Z	switch(*current)
Z	{
Z	case '\0':
Z		*local = '\0';
Z		current++;
Z/* $ */ goto emit;
Z	case '"' :
Z		*local++ = *current++;
Z/* $ */ goto doublequotes;
Z	case '/' :
Z		*local++ = '\\';
Z		current++;
Z/* $ */ goto charstate;;
Z	case '\'':
Z		*local++ = *current++;
Z/* $ */ goto singlequotes;
Z	case '\\':
Z		*local++ = *++current;
Z		current++;
Z/* $ */ goto charstate;
Z	case ';':
Z		*local = '\0';
Z		current++;
Z/* $ */ goto compound;
Z	case '|':
Z		*local = '\0';
Z		current++;
Z/* $ */ goto pipe;
Z	case '!':
Z		current++;
Z/* $ */ goto histstate;
Z	default:
Z		*local++ = *current++;
Z/* $ */ goto charstate;
Z	}
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate charstate
Z */
Z};
Z#line 163 "main.s"
Z
Z
Zemit:
Z{
Z#line 165 "main.s"
Z	if (inpipe)
Z	{
Z		inpipe = 0;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	command(localbuf);
Z/* $ */ goto done;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate emit
Z */
Z};
Z#line 174 "main.s"
Z
Z
Zcompound:
Z{
Z#line 176 "main.s"
Z	if (inpipe)
Z	{
Z		inpipe = 0;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	command(localbuf);
Z	local = localbuf;
Z	setmem(localbuf,sizeof(localbuf),0);	/* clear buffer */
Z/* $ */ goto eatwhitespace;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate compound
Z */
Z};
Z#line 187 "main.s"
Z
Z
Zsinglequotes:
Z{
Z#line 189 "main.s"
Z	switch (*current)
Z	{
Z	case '\0':
Z		write(2,"No closing quotes!!\r\n",21);
Z/* $ */ goto parserr;
Z	case '\'':
Z		*local++ = *current++;
Z/* $ */ goto charstate;
Z	default:
Z		*local++ = *current++;
Z/* $ */ goto singlequotes;
Z	}
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate singlequotes
Z */
Z};
Z#line 202 "main.s"
Z
Z
Zdoublequotes:
Z{
Z#line 204 "main.s"
Z	switch(*current)
Z	{
Z	case '\0':
Z		write(2,"No closing quotes!!\r\n",21);
Z/* $ */ goto done;
Z	case '"':
Z		*local++ = *current++;
Z/* $ */ goto charstate;
Z	default:
Z		*local++ = *current++;
Z/* $ */ goto doublequotes;
Z	}
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate doublequotes
Z */
Z};
Z#line 217 "main.s"
Z
Z
Zhiststate:
Z{
Z#line 219 "main.s"
Z	/* handle history substitutions */
Z	setmem(histbuf,sizeof(histbuf),0);	/* clear buffer */
Z
Z	/* save current pointer into command buffer */
Z	curr_save = current;
Z
Z	/* copy command head */
Z	strncpy(histbuf,cmdbuf,(int)(current-cmdbuf)-1);
Z
Z	/* takeline means take all arguments past current one */
Z	takeline = 0;
Z
Z	/* parse history expression */
Z	switch (*current)
Z	{
Z	case '!':	/* last command line */
Z		if (j)	/* special case first time through */
Z		{
Z			histindex = hiscount ? hiscount - 1 : histsize - 1;
Z		}
Z		else
Z		{
Z			/* force error condition */
Z			write(2,histerr,strlen(histerr));
Z/* $ */ goto parserr;
Z		}
Z		current++;	/* point to next */
Z		break;
Z	case '-':		/* negative (relative #) */
Z	/* a particular numbered command */
Z	case '0':
Z	case '1':
Z	case '2':
Z	case '3':
Z	case '4':
Z	case '5':
Z	case '6':
Z	case '7':
Z	case '8':
Z	case '9':
Z		/* repeat numbered command */
Z		repeat = atoi(current);
Z		if (repeat < 0)	/* handle relative addressing */
Z			repeat += j;
Z
Z		/* if command is within range */
Z		if ((j - repeat) <= histsize && repeat < j)
Z		{
Z			histindex = repeat % histsize;
Z		}
Z		else
Z		{
Z/* $ */ goto parserr;
Z		}
Z
Z		/* skip past numeric expression */
Z		while(isdigit(*current)||*current=='-')
Z			++current;
Z		break;
Z	default:
Z		write(2,"Bad history expression\r\n",24);
Z/* $ */ goto parserr;
Z	}
Z	/* look for particular argument substitutions */
Z	switch (*current)
Z	{
Z	/* we want the whole enchilada */
Z	case '\0':
Z	case '\t':
Z	case '\r':
Z	case '\n':
Z	case ' ':
Z		strcat(histbuf,history[histindex]);
Z		break;
Z	case ':':
Z		++current;	/* point past colon */
Z		switch (*current)
Z		{
Z		case '^':
Z			argindex = 1;
Z			++current;
Z			break;
Z		case '0':
Z		case '1':
Z		case '2':
Z		case '3':
Z		case '4':
Z		case '5':
Z		case '6':
Z		case '7':
Z		case '8':
Z		case '9':
Z			/* index of argument */ 
Z			argindex = atoi(current);
Z			while(isdigit(*current))
Z				++current;
Z			if (*current == '*')
Z			{
Z				takeline = 1;
Z				current++;
Z			}
Z			break;
Z		case '$':
Z			argindex = lastarg(history[histindex]);
Z			current++;
Z			break;
Z		case '*':
Z			takeline = 1;	/* take arg 1 through arg n */
Z			argindex = 1;
Z			current++;
Z			break;
Z		default:
Z/* $ */ goto parserr;
Z		}
Z		/* pick up pointer to argument in history we need */
Z		if (takeline == 0)
Z		{
Z			if (NULL == 
Z				(argptr = ntharg(history[histindex],argindex)))
Z			{
Z/* $ */ goto parserr;
Z			}
Z			strcat(histbuf,argptr);
Z		}
Z		else
Z		{
Z			while (NULL !=
Z				(argptr = ntharg(history[histindex],argindex++)))
Z			{
Z				strcat(histbuf,argptr);
Z				strcat(histbuf," ");
Z			}
Z		}
Z	}
Z	/* history substitutions */
Z	/* copy command buffer tail to tail buffer */
Z	strcpy(tail,current);
Z	/* copy histbuf back to cmdbuf */
Z	strcpy(cmdbuf,histbuf);
Z	/* point current at history substitution to continue parsing */
Z	current = --curr_save; /* -1 to backup over first ! */
Z	/* copy tail in */
Z	strcat(cmdbuf,tail);
Z	free(history[hiscount]);
Z	history[hiscount] = savestr(cmdbuf);
Z/* $ */ goto charstate;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate histstate
Z */
Z};
Z#line 366 "main.s"
Z
Z
Zpipe:
Z{
Z#line 368 "main.s"
Z	if (inpipe++)
Z	{
Z		inpipe = 1;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	strcat(localbuf," > ");
Z	currname ^= 1;
Z	strcat(localbuf,pipename[currname]);
Z	command(localbuf);
Z	local = localbuf;
Z	setmem(localbuf,sizeof(localbuf),0);
Z/* $ */ goto eatwhitespace;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate pipe
Z */
Z};
Z#line 382 "main.s"
Z
Z
Zeatwhitespace:
Z{
Z#line 384 "main.s"
Z/* strip out leading white space */
Zwhile(isspace(*current))
Z		current++;
Z	if (!*current)
Z/* $ */ goto parserr;
Z	else
Z/* $ */ goto charstate;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate eatwhitespace
Z */
Z};
Z#line 392 "main.s"
Z
Z
Zparserr:
Z{
Z#line 394 "main.s"
Z/* $ */ goto getline;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate parserr
Z */
Z};
Z#line 396 "main.s"
Z
Z
Zdone:
Z{
Z#line 398 "main.s"
Z	j++;	/* next command # */
Z/* $ */ goto getline;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate done
Z */
Z};
Z#line 401 "main.s"
Z
Z
Z/*
Z * BAD STATE LABEL
Z */
Zbadstate:
Z
Z	fprintf(stderr,"Fallen off end of a state!!!\n");
Z
Z	return -1;
Z
Z/*
Z * TERMINAL STATE LABEL
Z */
Zterminal:
Z
Z	return 0;
Z
Z/*
Z * end of state machine cli
Z */
Z}
Z
Zonintr()
Z{
Z	longjmp(env,-1);
Z}
Z
Zcommand(current)
Z	register char *current;
Z{
Z	extern do_prog();
Z	register int i;
Z	std_save();
Z	if (-1 == (i = findcmd(current)))
Z	{
Z		ctl_brk_setup();
Z		result = _Croot(current,do_prog);
Z		ctl_brk_restore();
Z	}
Z	else
Z	{
Z		if (-1 != setjmp(env))
Z		{
Z			signal(SIGINT,onintr);
Z			result = _Croot(current,commands[i].func);
Z		}
Z		signal(SIGINT,SIG_IGN);
Z	}
Z	std_restore();
Z}
Z
Zchar *
Zntharg(line,index)
Zregister char *line;
Z{
Z	register int i;
Z	static char buf[64];
Z	char *bptr;
Z	for (i = 0; *line;i++)
Z	{
Z		/* find start of arg[i] */
Z		while(*line && isspace(*line))
Z		{
Z			++line;
Z		}
Z		/* if this is start of requested arg, return pointer to it */
Z		if (i == index)
Z		{
Z			bptr = buf;
Z			while(*line && !isspace(*line))
Z				*bptr++ = *line++;
Z			*bptr = '\0';
Z			return buf;
Z		}
Z		/* find end of arg[i] */
Z		while(*line && !isspace(*line))
Z			++line;
Z	}
Z	return NULL;
Z}
Z
Zlastarg(line)
Zregister char *line;
Z{
Z	register int i;
Z
Z	for (i = 0; *line;i++)
Z	{
Z		/* find start of arg[i] */
Z		while(*line && isspace(*line))
Z			++line;
Z		/* find end of arg[i] */
Z		while(*line && !isspace(*line))
Z			++line;
Z	}
Z	return i-1;
Z}
STUNKYFLUFF
set `sum main.c`
if test 41066 != $1
then
echo main.c: Checksum error. Is: $1, should be: 41066.
fi
#
#
echo Extracting main.s:
sed 's/^Z//' >main.s <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <ctype.h>
Z#include <signal.h>
Z#include <fcntl.h>
Z#include <setjmp.h>
Ztypedef struct		/* used to find builtin commands */
Z{
Z	char *cmdname;
Z	int (*func)();
Z} builtin;
Z
Zextern builtin commands[];
Zextern char histerr[];
Zextern int j,hiscount;
Zextern char *history[];
Zextern int histsize;
Zextern int numcmds;
Zstatic int quiet = 0;
Z
Zchar *version = "SHELL VERSION 1.1 Kent Williams";
Z
Zjmp_buf env;
Z
Zchar *pipename[] =
Z{
Z	"\\shtmp1",
Z	"\\shtmp2"
Z};
Z
Zchar cmdbuf[512];
Zint  currname = 0;
Zint result = 0;
Z
Zmain(argc,argv)
Z	char *argv[];
Z{
Z	signal(SIGINT,SIG_IGN);	/* ignore breaks */
Z
Z	quiet = !isatty(0);		/* quiet = batch shell */
Z
Z	/* initialize local environment */
Z	init_env();
Z	cli();
Z	exit(0);
Z}
Z$nodebug		/* turn off state machine debugging */
Z$machine cli getline () 
Z$endargs
Z/* global variables */
Z	int i;
Z	int repeat, state, inpipe = 0;
Z	static char localbuf[256];
Z	static char histbuf[256];
Z	static char tail[256];
Z	int histindex,argindex,takeline;
Z	char *local = localbuf;
Z	char *current,*curr_save;
Z	char *ntharg(), *argptr;
Z	char *savestr();
Z
Z$state getline
Z		/* kill tmp files */
Z		unlink(pipename[0]); unlink(pipename[1]);
Z
Z		hiscount = j % histsize; /* hiscount is current position in history */
Z		if(!quiet)
Z			fprintf(stderr,"%d%% ",j);
Z
Z/*
Z * The following code simply reads a line from standard input.
Z * It is so complicated because when you save the standard stream
Z * files and execute another program/command, standard input is
Z * left in an uncertain state - the FILE stdin seems to be at EOF,
Z * even when standard input is associated with the console, and
Z * cr/lf combinations show up as line terminators, whereas usually
Z * only linefeeds get placed in the input stream.
Z * WHY? beats me.  Something could be wrong with
Z *  1. AZTEC C runtime
Z *  2. PCDOS
Z *  3. Me
Z *  4. All three, or permutations of 1-3 reducto ad absurdum.
Z * All I know is this works
Z */
Z		/* clear command buffer so string read is null terminated */
Z		setmem(cmdbuf,sizeof(cmdbuf),0);
Z		for (current = cmdbuf;;current++)
Z		{
Z			int readresult;
Z			if ((readresult = read(0,current,1)) == 0 ||
Z				readresult == -1)
Z			{
Z				$nextstate terminal
Z			}
Z			if (*current == '\r')
Z			{
Z				if ((readresult = read(0,current,1)) == 0 ||
Z					readresult == -1)
Z				{
Z					$nextstate terminal
Z				}
Z				*current = '\0';
Z				break;
Z			}
Z			else if (*current == '\n')
Z			{
Z				*current = '\0';	/* terminate string */
Z				break;
Z			}
Z		}
Z		current = cmdbuf;	/* point current at start of buffer */
Z/*
Z * end of input weirdness
Z */
Z		/* if we're recycling history strings, free previous one */
Z		if (history[hiscount])
Z			free(history[hiscount]);
Z
Z		/* save current in history array */
Z		history[hiscount] = savestr(current);
Z		/* parse command for compound statements and pipes */
Z		local = localbuf;	/* set pointer to state of buffer */
Z		setmem(localbuf,sizeof(localbuf),0);	/* clear buffer */
Z		$nextstate eatwhitespace 
Z$endstate getline
Z
Z$state charstate
Z	switch(*current)
Z	{
Z	case '\0':
Z		*local = '\0';
Z		current++;
Z		$nextstate emit
Z	case '"' :
Z		*local++ = *current++;
Z		$nextstate doublequotes
Z	case '/' :
Z		*local++ = '\\';
Z		current++;
Z		$nextstate charstate;
Z	case '\'':
Z		*local++ = *current++;
Z		$nextstate singlequotes
Z	case '\\':
Z		*local++ = *++current;
Z		current++;
Z		$nextstate charstate
Z	case ';':
Z		*local = '\0';
Z		current++;
Z		$nextstate compound
Z	case '|':
Z		*local = '\0';
Z		current++;
Z		$nextstate pipe
Z	case '!':
Z		current++;
Z		$nextstate histstate
Z	default:
Z		*local++ = *current++;
Z		$nextstate charstate
Z	}
Z$endstate charstate
Z
Z$state emit
Z	if (inpipe)
Z	{
Z		inpipe = 0;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	command(localbuf);
Z	$nextstate done
Z$endstate emit
Z
Z$state compound
Z	if (inpipe)
Z	{
Z		inpipe = 0;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	command(localbuf);
Z	local = localbuf;
Z	setmem(localbuf,sizeof(localbuf),0);	/* clear buffer */
Z	$nextstate eatwhitespace
Z$endstate compound
Z
Z$state singlequotes
Z	switch (*current)
Z	{
Z	case '\0':
Z		write(2,"No closing quotes!!\r\n",21);
Z		$nextstate parserr
Z	case '\'':
Z		*local++ = *current++;
Z		$nextstate charstate
Z	default:
Z		*local++ = *current++;
Z		$nextstate singlequotes
Z	}
Z$endstate singlequotes
Z
Z$state doublequotes
Z	switch(*current)
Z	{
Z	case '\0':
Z		write(2,"No closing quotes!!\r\n",21);
Z		$nextstate done
Z	case '"':
Z		*local++ = *current++;
Z		$nextstate charstate
Z	default:
Z		*local++ = *current++;
Z		$nextstate doublequotes
Z	}
Z$endstate doublequotes
Z
Z$state histstate
Z	/* handle history substitutions */
Z	setmem(histbuf,sizeof(histbuf),0);	/* clear buffer */
Z
Z	/* save current pointer into command buffer */
Z	curr_save = current;
Z
Z	/* copy command head */
Z	strncpy(histbuf,cmdbuf,(int)(current-cmdbuf)-1);
Z
Z	/* takeline means take all arguments past current one */
Z	takeline = 0;
Z
Z	/* parse history expression */
Z	switch (*current)
Z	{
Z	case '!':	/* last command line */
Z		if (j)	/* special case first time through */
Z		{
Z			histindex = hiscount ? hiscount - 1 : histsize - 1;
Z		}
Z		else
Z		{
Z			/* force error condition */
Z			write(2,histerr,strlen(histerr));
Z			$nextstate parserr
Z		}
Z		current++;	/* point to next */
Z		break;
Z	case '-':		/* negative (relative #) */
Z	/* a particular numbered command */
Z	case '0':
Z	case '1':
Z	case '2':
Z	case '3':
Z	case '4':
Z	case '5':
Z	case '6':
Z	case '7':
Z	case '8':
Z	case '9':
Z		/* repeat numbered command */
Z		repeat = atoi(current);
Z		if (repeat < 0)	/* handle relative addressing */
Z			repeat += j;
Z
Z		/* if command is within range */
Z		if ((j - repeat) <= histsize && repeat < j)
Z		{
Z			histindex = repeat % histsize;
Z		}
Z		else
Z		{
Z			$nextstate parserr
Z		}
Z
Z		/* skip past numeric expression */
Z		while(isdigit(*current)||*current=='-')
Z			++current;
Z		break;
Z	default:
Z		write(2,"Bad history expression\r\n",24);
Z		$nextstate parserr
Z	}
Z	/* look for particular argument substitutions */
Z	switch (*current)
Z	{
Z	/* we want the whole enchilada */
Z	case '\0':
Z	case '\t':
Z	case '\r':
Z	case '\n':
Z	case ' ':
Z		strcat(histbuf,history[histindex]);
Z		break;
Z	case ':':
Z		++current;	/* point past colon */
Z		switch (*current)
Z		{
Z		case '^':
Z			argindex = 1;
Z			++current;
Z			break;
Z		case '0':
Z		case '1':
Z		case '2':
Z		case '3':
Z		case '4':
Z		case '5':
Z		case '6':
Z		case '7':
Z		case '8':
Z		case '9':
Z			/* index of argument */ 
Z			argindex = atoi(current);
Z			while(isdigit(*current))
Z				++current;
Z			if (*current == '*')
Z			{
Z				takeline = 1;
Z				current++;
Z			}
Z			break;
Z		case '$':
Z			argindex = lastarg(history[histindex]);
Z			current++;
Z			break;
Z		case '*':
Z			takeline = 1;	/* take arg 1 through arg n */
Z			argindex = 1;
Z			current++;
Z			break;
Z		default:
Z			$nextstate parserr
Z		}
Z		/* pick up pointer to argument in history we need */
Z		if (takeline == 0)
Z		{
Z			if (NULL == 
Z				(argptr = ntharg(history[histindex],argindex)))
Z			{
Z				$nextstate parserr
Z			}
Z			strcat(histbuf,argptr);
Z		}
Z		else
Z		{
Z			while (NULL !=
Z				(argptr = ntharg(history[histindex],argindex++)))
Z			{
Z				strcat(histbuf,argptr);
Z				strcat(histbuf," ");
Z			}
Z		}
Z	}
Z	/* history substitutions */
Z	/* copy command buffer tail to tail buffer */
Z	strcpy(tail,current);
Z	/* copy histbuf back to cmdbuf */
Z	strcpy(cmdbuf,histbuf);
Z	/* point current at history substitution to continue parsing */
Z	current = --curr_save; /* -1 to backup over first ! */
Z	/* copy tail in */
Z	strcat(cmdbuf,tail);
Z	free(history[hiscount]);
Z	history[hiscount] = savestr(cmdbuf);
Z	$nextstate charstate
Z$endstate histstate
Z
Z$state pipe
Z	if (inpipe++)
Z	{
Z		inpipe = 1;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	strcat(localbuf," > ");
Z	currname ^= 1;
Z	strcat(localbuf,pipename[currname]);
Z	command(localbuf);
Z	local = localbuf;
Z	setmem(localbuf,sizeof(localbuf),0);
Z	$nextstate eatwhitespace
Z$endstate pipe
Z
Z$state eatwhitespace
Z/* strip out leading white space */
Zwhile(isspace(*current))
Z		current++;
Z	if (!*current)
Z		$nextstate parserr
Z	else
Z		$nextstate charstate
Z$endstate eatwhitespace
Z
Z$state parserr
Z	$nextstate getline
Z$endstate parserr
Z
Z$state done
Z	j++;	/* next command # */
Z	$nextstate getline
Z$endstate done
Z
Z$endmachine cli
Z
Zonintr()
Z{
Z	longjmp(env,-1);
Z}
Z
Zcommand(current)
Z	register char *current;
Z{
Z	extern do_prog();
Z	register int i;
Z	std_save();
Z	if (-1 == (i = findcmd(current)))
Z	{
Z		ctl_brk_setup();
Z		result = _Croot(current,do_prog);
Z		ctl_brk_restore();
Z	}
Z	else
Z	{
Z		if (-1 != setjmp(env))
Z		{
Z			signal(SIGINT,onintr);
Z			result = _Croot(current,commands[i].func);
Z		}
Z		signal(SIGINT,SIG_IGN);
Z	}
Z	std_restore();
Z}
Z
Zchar *
Zntharg(line,index)
Zregister char *line;
Z{
Z	register int i;
Z	static char buf[64];
Z	char *bptr;
Z	for (i = 0; *line;i++)
Z	{
Z		/* find start of arg[i] */
Z		while(*line && isspace(*line))
Z		{
Z			++line;
Z		}
Z		/* if this is start of requested arg, return pointer to it */
Z		if (i == index)
Z		{
Z			bptr = buf;
Z			while(*line && !isspace(*line))
Z				*bptr++ = *line++;
Z			*bptr = '\0';
Z			return buf;
Z		}
Z		/* find end of arg[i] */
Z		while(*line && !isspace(*line))
Z			++line;
Z	}
Z	return NULL;
Z}
Z
Zlastarg(line)
Zregister char *line;
Z{
Z	register int i;
Z
Z	for (i = 0; *line;i++)
Z	{
Z		/* find start of arg[i] */
Z		while(*line && isspace(*line))
Z			++line;
Z		/* find end of arg[i] */
Z		while(*line && !isspace(*line))
Z			++line;
Z	}
Z	return i-1;
Z}
STUNKYFLUFF
set `sum main.s`
if test 47403 != $1
then
echo main.s: Checksum error. Is: $1, should be: 47403.
fi
#
#
echo Extracting makefile:
sed 's/^Z//' >makefile <<\STUNKYFLUFF
Z# home for all commands
ZBINDIR=\\bin\\
Z# small model library path
ZCLIB=-l/clibs/c
Z
Z# source files for shell
Zshsrc=main.c cd.c cp.c doprog.c fexecvp.c more.c cmds.c chmod.c y.c fexec.asm \
Zinvalid.c ls.c pushd.c fgrep.c md.c mv.c pwd.c rm.c crlf.c drive.c dump2.c \
Zrmdir.c savestr.c stdsave.c touch.c _croot.c env.c fexecv.c cat.c echo.c \
Zcmdlist.c ctlbrk.asm mvcp.c
Z
Z# object files for shell
Zshobj=main.o cd.o cp.o doprog.o fexecvp.o more.o cmds.o chmod.o y.o fexec.o \
Zinvalid.o ls.o pushd.o fgrep.o md.o mv.o pwd.o rm.o crlf.o drive.o dump2.o \
Zrmdir.o savestr.o stdsave.o touch.o _croot.o env.o fexecv.o cat.o echo.o \
Zcmdlist.o ctlbrk.o mvcp.o
Z
Zmain.c : main.s
Z	statecom main
Z
Z#
Z# make shell - check currency of all percursors, link and then move to
Z# bin directory
Z#
Zshell.exe : $(shobj) 
Z	ln -t -o $@ -f shell.lnk
Z	\\atron\\aztoat <shell.sym >shell.map
Z	chmod +w $(BINDIR)$@
Z	mv $@ $(BINDIR)$@
Z	chmod -w $(BINDIR)$@
Z
Z#
Z# make entries to produce stand-alone versions of shell builtins
Z#
Zchmod.com : 
Z	cc -dMAIN $*
Z	ln -o $@ $*.o -l/clibs/c
Z	rm $*.o
Z	chmod +w $(BINDIR)chmod.com
Z	mv chmod.com $(BINDIR)chmod.com
Z	chmod -w $(BINDIR)chmod.com
Z
Zmore.com : 
Z	cc -dMAIN $*
Z	ln -o $@ $*.o -l/clibs/s -l/clibs/c
Z	rm $*.o
Z	chmod +w $(BINDIR)$@
Z	mv $@ $(BINDIR)$@
Z	chmod -w $(BINDIR)$@
Z
Zmv.com : croot.o savestr.o mvcp.o
Z	cc -dMAIN $*
Z	ln -o $@ $*.o savestr.o croot.o mvcp.o  -l/clibs/c
Z	rm $*.o
Z	chmod +w $(BINDIR)$@
Z	mv $@ $(BINDIR)$@
Z	chmod -w $(BINDIR)$@
Z
Zcp.com : croot.o savestr.o mvcp.o
Z	cc -dMAIN $*
Z	ln -o $@ $*.o savestr.o croot.o  mvcp.o -l/clibs/c
Z	rm $*.o
Z	chmod +w $(BINDIR)$@
Z	mv $@ $(BINDIR)$@
Z	chmod -w $(BINDIR)$@
Z
Zls.com : 
Z	cc -dMAIN $*
Z	ln -o $@ $*.o -l/clibs/c
Z	rm $*.o
Z	chmod +w $(BINDIR)$@
Z	mv $@ $(BINDIR)$@
Z	chmod -w $(BINDIR)$@
Zeditall :
Z	z $(shsrc)
Zclean :
Z	rm *.o *.bak *.sym
STUNKYFLUFF
set `sum makefile`
if test 36319 != $1
then
echo makefile: Checksum error. Is: $1, should be: 36319.
fi
#
#
echo Extracting md.c:
sed 's/^Z//' >md.c <<\STUNKYFLUFF
Zmd(argc,argv)
Z	char *argv[];
Z{
Z	if (-1 == mkdir(*(++argv)))
Z	{
Z		perror("mkdir");
Z		return -1;
Z	}
Z	return 0;
Z}
STUNKYFLUFF
set `sum md.c`
if test 21756 != $1
then
echo md.c: Checksum error. Is: $1, should be: 21756.
fi
#
#
echo Extracting more.c:
sed 's/^Z//' >more.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <sgtty.h>
Z#include <debug.h>
Z#include <ctype.h>
Z#include <setjmp.h>
Z#include <signal.h>
Zvoid (*signal())();
Zvoid (*moresig)();
ZFILE *fdopen();
Z
Zlong int size, position;
Zchar filename[30];
Zchar isaconsole = '\0';
Zchar isapipe = '\0';
Zint tabsize = 8;
Z#define STDIN 0
Z#define STDOUT 1
Z#define STDERR 2
Z#define ESC '\033'
Z#define IBMPC			/* display code dependent on PC hardware/bios */
Z#ifdef IBMPC
Zextern void scr_putc();
Z#endif
Zvoid std_out(c)
Z{
Z	write(1,&c,1);
Z}
Zvoid (*output)();
Z#ifdef MAIN
Zcrlf()
Z{
Z	write(2,"\r\n",2);
Z}
Z#endif
Zjmp_buf moreenv;
Zmoreintr()
Z{
Z	signal(SIGINT,SIG_IGN);	/* ignore signals until we finish putting stuff
Z	                           back in order */
Z	longjmp(moreenv,-1);
Z}
Z#ifdef MAIN
Zmain
Z#else
Zmore
Z#endif
Z(argc,argv)
Z	int argc;
Z	char *argv[];
Z{
Z	FILE *fp, *fopen();
Z	long ftell();
Z	long int i;
Z	long int fsize();
Z
Z	if (-1 == setjmp(moreenv))
Z	{
Z		write(2,"Interrupted\r\n",13);
Z		fclose(fp);
Z		signal(SIGINT,moresig);
Z		return -1;
Z	}
Z
Z	moresig = signal(SIGINT,moreintr);
Z	isaconsole = isatty(STDOUT);
Z	isapipe = !isatty(STDIN);
Z#ifdef IBMPC
Z	if (isaconsole)
Z	{
Z		output = scr_putc;
Z		scr_echo(0);
Z	}
Z	else
Z#endif
Z		output = std_out;
Z	if ( (*(++argv))[0] == '-' && isdigit((*argv)[1]) )
Z	{
Z		tabsize = atoi( (*argv+1) );
Z		--argc;
Z	} else
Z	{
Z		--argv;
Z	}
Z	if (argc == 1)
Z	{
Z		if (NULL == (fp = fdopen(0,"r")))
Z		{
Z			return -1;
Z		}
Z		display(fp);
Z		crlf();
Z		return 0;
Z	}
Z	while(--argc) 
Z	{
Z		if (NULL == (fp = fopen(*(++argv),"r")) )
Z		{
Z			fprintf(stderr,"more - can't open %s\n",*argv);
Z			continue;
Z		}
Z		strncpy(filename,*argv,30);
Z		if (filename[29])
Z			filename[29] = '\0';
Z		if (!isapipe)
Z			size = fsize(fp);
Z		position = 0;
Z		if (-2 == display(fp))
Z			argc = 0;		/* force completion of command */
Z		fclose(fp);
Z	}
Zbugout:
Z#ifdef IBMPC
Z	if (isaconsole)
Z		scr_echo(0);
Z#endif
Z	crlf();
Z	tabsize = 8;
Z	isaconsole = isapipe = '\0';
Z	signal(SIGINT,moresig);
Z	return 0;
Z}
Z
Zlong fsize(fp) 
Z	FILE *fp;
Z{
Z	long ftell();
Z	long position, last;
Z	position = ftell(fp);
Z	if (-1 == fseek(fp,0L,2))
Z	{
Z		fprintf(stderr,"more - error on fseek\n");
Z	}
Z	last = (ftell(fp));
Z	fseek(fp,position,0);
Z	return last;
Z}
Z#define LBUFSIZE 160
Zchar linebuffer[LBUFSIZE];
Zint lines;
Z
Zdisplay(fp)
Z	FILE *fp;
Z{
Z	FILE *fgets();
Z	long ftell();
Z	char c;
Z	lines = 1;
Z	while ( NULL != fgets(linebuffer,LBUFSIZE,fp))
Z	{
Z		if (isaconsole)
Z		{
Z			if (lines == 1)		/* top of display */
Z			{
Z#ifdef IBMPC
Z				scr_clear();
Z#else
Z/* ansi terminal screen clear */
Z				printf("%c[2J",ESC);	/* clear display */
Z#endif
Z			}
Z		}
Z		lines += localputs(linebuffer);
Z		if (isaconsole)
Z		{
Z			if (lines >=  24)		/* bottome of display */
Z			{
Z				char tst;
Z				position = ftell(fp);
Z#ifdef IBMPC
Z				scr_curs(24,0);
Z#else
Z/* ansi terminal positioning */
Z				printf("%c[25;1H%c[7m",ESC,ESC);
Z#endif
Z				if (!isapipe)
Z#ifdef IBMPC
Z					scr_printf(
Z#else
Z					fprintf(stderr,
Z#endif
Z				"%s - %ld bytes - %d%% displayed - <ESC> = skip to next file",
Z					filename,size,percent(position,size) );
Z				else
Z#ifdef IBMPC
Z					scr_printf(
Z#else
Z					fprintf(stderr,
Z#endif
Z									"-more-");
Z				switch (bdos(7,0,0))	/* get a character no echo */
Z				{
Z				case ESC :
Z					return 0;
Z				case 3 :
Z					return -2;
Z				default:
Z					break;
Z				}
Z
Z				lines = 1;
Z			}
Z		}
Z	}
Z	if (isaconsole)
Z	{
Z		if (lines != 1)		/* bottome of display */
Z		{
Z#ifdef IBMPC
Z			scr_curs(24,0);
Z#else
Z/* ansi terminal positioning */
Z				printf("%c[25;1H%c[7m",ESC,ESC);
Z#endif
Z			if (!isapipe)
Z			{
Z				position = ftell(fp);
Z#ifdef IBMPC
Z				scr_printf(
Z#else
Z				fprintf(stderr,
Z#endif
Z						"%s - %ld characters - %d%% displayed",
Z					filename,size,percent(position,size) );
Z			}
Z			else
Z#ifdef IBMPC
Z				scr_printf(
Z#else
Z				fprintf(stderr,
Z#endif
Z								"-done-");
Z			bdos(7,0,0); /* console input no echo */
Z			lines = 1;
Z		}
Z	}
Z}
Z
Zpercent(x,y)
Z	long int x,y;
Z{	/* returns integer percentage of x into y */
Z#ifdef FLOAT
Z	float xf,yf;
Z	xf = x; yf = y;
Z	x = ((xf/yf)*100);
Z#endif
Z	x *= 100;
Z	if (y)
Z		x /= y;
Z	else
Z		x = 100;
Z	return (x);
Z}
Z
Zlocalputs(lb)
Z	register char *lb;
Z{
Z	int lines, pos, tabstop;
Z	lines = 1;
Z	pos = 0;
Z	while (*lb)
Z	{
Z		switch (*lb)
Z		{
Z		case '\t':
Z			tabstop = pos + (tabsize - (pos % tabsize));	
Z			for (;pos <= tabstop; pos++)
Z				(*output)(' ');
Z			break;
Z		case '\n':
Z			(*output)('\r');
Z		default:
Z			(*output)(*lb);
Z			pos++;
Z		}
Z		if (pos == 79)
Z		{
Z			pos = 1;
Z			(*output)('\r');
Z			(*output)('\n');
Z			++lines;
Z		} else if (pos > 79)
Z		{
Z			pos -= 80;
Z			++lines;
Z		}
Z		++lb;
Z	}
Z	return lines;
Z}
Z
Z#ifdef IBMPC
Zscr_printf(fmt,args)
Zchar *fmt; unsigned args;
Z{
Z	format(scr_putc,fmt,&args);
Z}
Z#endif
STUNKYFLUFF
set `sum more.c`
if test 34929 != $1
then
echo more.c: Checksum error. Is: $1, should be: 34929.
fi
#
#
echo Extracting mv.c:
sed 's/^Z//' >mv.c <<\STUNKYFLUFF
Z/* :ts=2 */
Z#include <stdio.h>
Zchar *savestr();
Z#ifndef MAIN
Zextern char *fname_part(),*savestr();
Zextern char *me;
Z#else
Zchar *me;
Z#endif
Z/* mv.c - implements a version of UNIX mv */
Z#ifdef MAIN
Zmain
Z#else
Zmv
Z#endif
Z(argc,argv)
Z	int argc;
Z	register char *argv[];
Z{
Z	static char *usage = "mv : usage mv file1 [file2 . . fileN] target\r\n";
Z	static char target_name[128];
Z	char target[128],*fname_part();
Z	register int i;
Z	int len;
Z#ifndef MAIN
Z	me = argv[0];	/* referenced in routines common with cp.c */
Z#endif
Z	if (argc < 3)
Z	{
Z		write(2,usage,strlen(usage));
Z		return(-1);
Z	}
Z	strcpy(target, argv[argc-1]);
Z	/* kill trailing backslashes */
Z	if (target[i = strlen(target) - 1] == '\\')
Z		target[i] = '\0';
Z	if (argc == 3)
Z	{
Z		/* if the target doesn't exist and it's not a directory then rename */
Z		if (!filep(target))
Z		{
Z			if (!dirp(target))
Z			{
Z				fprintf(stderr,"rename : moving %s to %s\n"
Z				,argv[1],argv[2]);
Z				rename(argv[1],argv[2]);
Z			}
Z			else
Z			{
Z			/* if the target is a directory copy to same name that directory */
Z				strcpy(target_name,target);
Z				if (target_name[(len = strlen(target_name))-1] != '\\')
Z				{
Z					target_name[len = strlen(target_name)] = '\\';
Z					target_name[len+1] = '\0';
Z				}
Z				strcat(target_name,fname_part(argv[1]));
Z#ifdef DEBUG
Z				fprintf(stderr,"interdirectory copy same name : moving %s to %s\n"
Z#else
Z				fprintf(stderr,"moving %s to %s\n"
Z#endif
Z				,argv[1],target_name);
Z				if (-1 != maybecopy(target_name,argv[1]))
Z					unlink(argv[1]);
Z			}
Z		}
Z		else
Z		{
Z		/* target exists , and isn't a directory */
Z			char *tpath,*spath;
Z			char tpathsaved = 0,spathsaved = 0;
Z			char *path_part();
Z			/* find path parts of source and target */
Z			if (tpath = path_part(target))
Z			{
Z				tpath = savestr(tpath);
Z				tpathsaved++;
Z			}
Z			else
Z				tpath = ".";	/* current directory */
Z			if (spath = path_part(argv[1]))
Z			{
Z				spath = savestr(spath);
Z				spathsaved++;
Z			}
Z			else
Z				spath = ".";	/* current directory */
Z			if (0 == strcmp(tpath,spath))
Z			{
Z			/* if source and target are in the same directory */
Z#ifdef DEBUG
Z				fprintf(stderr,"intradirectory delete then rename : moving %s to %s\n"
Z#else
Z				fprintf(stderr,"moving %s to %s\n"
Z#endif
Z				,argv[1],target);
Z				unlink(target);
Z				rename(argv[1],target);
Z			}
Z			else
Z			{
Z#ifdef DEBUG
Z				fprintf(stderr,"interdirectory file to file: moving %s to %s\n"
Z#else
Z				fprintf(stderr,"moving %s to %s\n"
Z#endif
Z				,argv[1],target);
Z				if (-1 != maybecopy(target,argv[1]))
Z					unlink(argv[1]);
Z			}
Z			if (tpathsaved)
Z				free(tpath);
Z			if (spathsaved)
Z				free(spath);
Z		}
Z		return(0);
Z	}
Z	/* handle special case of a drive designation */
Z	if (target[(i = strlen(target))-1] != ':')
Z		if (!dirp(target))
Z		{
Z			fprintf(stderr,"mv : %s isn't a directory\n",target);
Z			return(-1);
Z		}
Z	for (i = 1; i < argc-1; i++)
Z	{
Z		strcpy(target_name,target);
Z		if (target_name[(len = strlen(target_name))-1] != '\\')
Z		{
Z			target_name[len = strlen(target_name)] = '\\';
Z			target_name[len+1] = '\0';
Z		}
Z		strcat(target_name,fname_part(argv[i]));
Z		if (!filep(argv[i]))
Z		{
Z			fprintf(stderr,"mv : %s isn't a file\n",argv[i]);
Z			continue;
Z		}
Z		fprintf(stderr,"moving %s to %s\n",argv[i],target_name);
Z		if (-1 != maybecopy(target_name,argv[i]))
Z			unlink(argv[i]);
Z	}
Z	return 0;
Z}
Z
Zmaybecopy(target,source)
Z	char *target,*source;
Z{
Z	char *drive_part();
Z	static char targetdrive[3], sourcedrive[3];
Z	strcpy(targetdrive,drive_part(target));
Z	strcpy(sourcedrive,drive_part(source));
Z	if (0 == strcmp(targetdrive,sourcedrive))
Z	{
Z		unlink(target);
Z		rename(source,target);
Z		return 0;
Z	}
Z	return (filecopy(target,source));
Z}
STUNKYFLUFF
set `sum mv.c`
if test 37323 != $1
then
echo mv.c: Checksum error. Is: $1, should be: 37323.
fi
#
#
echo Extracting mvcp.c:
sed 's/^Z//' >mvcp.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z/*
Z * mvcp.c - routines common to mv and cp
Z */
Z#include <fcntl.h>
Zchar buffer[BUFSIZ*16];
Zextern char *me;
Zfilecopy(target,source)
Zchar *target,*source;
Z{
Z    int t,s,r;
Z    if (-1 == (s = open(source,O_RDONLY)))
Z    {
Z		fprintf(stderr,"%s : can't open %s\n",me,source);
Z		return(-1);
Z    }
Z    if (-1 == (t = open(target,O_TRUNC)))
Z    {
Z		fprintf(stderr,"%s : can't open %s\n",me,target);
Z		return(-1);
Z    }
Z    while(0 != (r = read(s,buffer,BUFSIZ*16)) && r != -1)
Z    {
Z		if(-1 == write(t,buffer,r))
Z		{
Z		    fprintf(stderr,"%s : error writing %s\n",me,target);
Z		    return(-1);
Z		}
Z    }
Z    close(t); 
Z    close(s);
Z	return (0);
Z}
Z
Z#include <errno.h>
Ztypedef struct
Z{
Z    char dos_reserved[21];
Z    char attribute;
Z    unsigned file_time;
Z    unsigned file_date;
Z    long file_size;
Z    char file_name[13];
Z} 
Zfcb;
Zfcb dir;
Z
Zdirp(s)
Zchar *s;
Z{
Z	register int junk1,junk2;
Z	/* handle all of the stupid special cases */
Z	if ((s[1] == ':' && s[2] == '\0')	/* root directory on a drive */
Z		|| (s[1] == '\0')				/* root directory default drive */
Z	)
Z	{
Z		return 1;
Z	}
Z	if (0 == strcmp(s,".."))			/* parent of this directory */
Z	{
Z		int returnval;
Z		char *current,*parent;
Z		current = getcwd(NULL,64);
Z		if (-1 == chdir(s)) /* go to parent */
Z			returnval = 0;
Z		else
Z			returnval = 1;
Z		parent = getcwd(NULL,64);
Z		chdir(current);
Z		free(current); free(parent);
Z		return returnval;
Z	}
Z    /* set the disk transfer address */
Z    bdos(0x1A,&dir);
Z    /* do a search first for the directory path */
Z    return (bdos(0x4E,s,0x10) == 0 && bdos(0x4E,s,0) != 0);
Z}
Z
Zfilep(s)
Zchar *s;
Z{
Z    /* set the disk transfer address */
Z    bdos(0x1A,&dir);
Z    /* do a search first for the directory path */
Z    return bdos(0x4E,s,0) == 0;
Z}
Zchar *fname_part(s)
Zregister char *s;
Z{
Z    register char *r; 
Z    char *rindex();
Z    if (r = rindex(s,'\\'))
Z	{
Z		return r+1;
Z	}
Z    if (r = rindex(s,':'))
Z	{
Z		return r+1;
Z	}
Z    return s;
Z}
Zchar *path_part(s)
Zregister char *s;
Z{
Z	static char buffer[64];
Z    register char *r; 
Z    char *rindex();
Z	strcpy(buffer,s);	/* copy string */
Z	if (r = rindex(buffer,'\\'))
Z	{
Z		*++r = '\0';
Z		return buffer;
Z	}
Z	if (r = rindex(buffer,':'))
Z	{
Z		*++r = '\0';
Z		return buffer;
Z	}
Z	return NULL;
Z}
Zchar *drive_part(s)
Zregister char *s;
Z{
Z	static char buffer[64];
Z    register char *r; 
Z    char *rindex();
Z	strcpy(buffer,s);	/* copy string */
Z	if (buffer[1] == ':')
Z	{
Z		buffer[2] = '\0';
Z		return buffer;
Z	}
Z	return NULL;
Z}
STUNKYFLUFF
set `sum mvcp.c`
if test 16416 != $1
then
echo mvcp.c: Checksum error. Is: $1, should be: 16416.
fi
#
#
echo Extracting pushd.c:
sed 's/^Z//' >pushd.c <<\STUNKYFLUFF
Z#define NULL (void *)0
Z
Zstatic char *dirstack[10];
Zstatic int dsp = -1;
Z
Zpushd(argc,argv)
Zchar *argv[];
Z{
Z	char *getcwd(), *savestr();
Z	static char *usage = "usage : pushd newdir";
Z	static char *pusherr = "pushd : dir stack overflow";
Z	char dirbuf[64];
Z	if (argc == 1)
Z	{
Z		write(2,usage,strlen(usage));
Z		crlf();
Z		return -1;
Z	}
Z	if (NULL ==  getcwd(&dirbuf[1],64))
Z	{
Z		perror("pushd");
Z		return -1;
Z	}
Z	if (++dsp == 10)
Z	{
Z		write(2,pusherr,strlen(pusherr));
Z		crlf();
Z		return -1;
Z	}
Z	dirbuf[0] = '\\';
Z	if (-1 == chdir(*(++argv)))
Z	{
Z		--dsp;
Z		perror("pushd");
Z		return -1;
Z	}
Z	dirstack[dsp] = savestr(dirbuf);
Z	return 0;
Z}
Z
Zpopd()
Z{
Z	register int returnval = 0;
Z	static char *poperr = "popd : dir stack underflow";
Z	if (dsp == -1)
Z	{
Z		write(2,poperr,strlen(poperr));
Z		crlf();
Z		return -1;
Z	}
Z	if (-1 == chdir(dirstack[dsp]))
Z	{
Z		perror("popd");
Z		write(2,dirstack[dsp],strlen(dirstack[dsp]));
Z		crlf();
Z		returnval = -1;
Z	}
Z	free(dirstack[dsp--]);
Z	return returnval;
Z}
STUNKYFLUFF
set `sum pushd.c`
if test 22547 != $1
then
echo pushd.c: Checksum error. Is: $1, should be: 22547.
fi
#
#
echo Extracting pwd.c:
sed 's/^Z//' >pwd.c <<\STUNKYFLUFF
Z#define NULL (void *)0
Zpwd(argc,argv)
Zchar *argv[];
Z{
Z	char *getcwd();
Z	register char *dir;
Z	if (!(dir = getcwd(NULL,256)))
Z	{
Z		perror("pwd");
Z		return -1;
Z	}
Z	write(1,"\\",1);
Z	write(1,dir,strlen(dir));
Z	crlf();
Z	if (dir)
Z		free(dir);
Z	return 0;
Z}
STUNKYFLUFF
set `sum pwd.c`
if test 11133 != $1
then
echo pwd.c: Checksum error. Is: $1, should be: 11133.
fi
#
#
echo Extracting rm.c:
sed 's/^Z//' >rm.c <<\STUNKYFLUFF
Z#include <stdio.h>
Zrm(argc,argv)
Z	int argc;
Z	char *argv[];
Z{
Z	extern int _echo;
Z	int oldecho = _echo;
Z	char ask = 0;
Z
Z	if (argv[1][0] == '-' && toupper(argv[1][1]) == 'Q')
Z	{
Z		ask++;
Z		_echo = 1;
Z		++argv; --argc;
Z	}
Z	while(--argc) 
Z	{
Z		++argv;
Z		if (ask)
Z		{
Z			fprintf(stderr,"delete %s? (y or n) : ",*argv);
Z			if (toupper(scr_getc()) != 'Y')
Z			{
Z				write(2,"\r\n",2);
Z				continue;
Z			}
Z			write(2,"\r\n",2);
Z		}
Z		if (-1 == unlink(*(argv)))
Z		{
Z			perror("rm");
Z		}
Z	}
Z	_echo = oldecho;
Z}
Z
Z
STUNKYFLUFF
set `sum rm.c`
if test 00071 != $1
then
echo rm.c: Checksum error. Is: $1, should be: 00071.
fi
#
#
echo Extracting rmdir.c:
sed 's/^Z//' >rmdir.c <<\STUNKYFLUFF
Z#include <stdio.h>
Zrd(argc,argv)
Zchar *argv[];
Z{
Z	static char *usage = "usage : rmdir directory";
Z	if (argc == 1)
Z		write(2,usage,strlen(usage));
Z	if (-1 == rmdir(*(++argv)))
Z	{
Z		perror("rmdir");
Z		return -1;
Z	}
Z	return 0;
Z}
STUNKYFLUFF
set `sum rmdir.c`
if test 22459 != $1
then
echo rmdir.c: Checksum error. Is: $1, should be: 22459.
fi
#
#
echo Extracting savestr.c:
sed 's/^Z//' >savestr.c <<\STUNKYFLUFF
Z#define NULL (void *)0
Zchar *
Zsavestr(s)
Z	register char *s;
Z{
Z	register char *r,*malloc();
Z	/* squirrel away matched file name */
Z	if (NULL == (r = malloc(strlen(s)+1)))
Z		abort();
Z	strcpy(r,s);
Z	r[strlen(s)] = '\0';
Z	return r;
Z}
STUNKYFLUFF
set `sum savestr.c`
if test 32164 != $1
then
echo savestr.c: Checksum error. Is: $1, should be: 32164.
fi
#
#
echo Extracting shell.lnk:
sed 's/^Z//' >shell.lnk <<\STUNKYFLUFF
Zmain.o cd.o cp.o doprog.o fexecvp.o more.o
Zinvalid.o ls.o pushd.o fgrep.o env.o cmds.o chmod.o
Zmd.o mv.o pwd.o rm.o crlf.o drive.o fexecv.o
Zrmdir.o savestr.o stdsave.o touch.o dump2.o mvcp.o
Z_croot.o cat.o echo.o y.o fexec.o cmdlist.o ctlbrk.o
Z/clibs/c.lib /clibs/s.lib
STUNKYFLUFF
set `sum shell.lnk`
if test 41375 != $1
then
echo shell.lnk: Checksum error. Is: $1, should be: 41375.
fi
#
#
echo Extracting ssbrk.asm:
sed 's/^Z//' >ssbrk.asm <<\STUNKYFLUFF
Z; :ts=8
Z;Copyright (C) 1983 by Manx Software Systems
Z	include lmacros.h
Zdataseg	segment	word public 'data'
Z	extrn	$MEMRY:word
Z	extrn	_mbot_:word, _sbot_:word
Z	extrn	_mtop_:word
Z	extrn	errno_:word
Z	extrn	_STKLOW_:word
Z	extrn	_PSP_:word
Zdataseg	ends
Z	assume	ds:dataseg
Z;
Z; sbrk(size): return address of current top & bump by size bytes
Z;
Z	procdef	sbrk,<<siz,word>>
Z	push	di
Z	mov	ax,siz
Z	mov	di,$MEMRY
Z	add	ax,di
Z	push	ax
Z	call	brk_
Z	pop	cx
Z	test	ax,ax
Z	jnz	brk_error
Z	mov	ax,di		;return original value of the break
Zbrk_error:
Z	pop	di
Z	test	ax,ax		;set flags for C
Z	pret
Z	pend	sbrk
Z;
Z; brk(addr):	set current top address to addr
Z;		returns 0 if ok, -1 if error
Z;
Z	procdef brk,<<addr,word>>
Z	mov	ax,addr
Z	inc	ax
Z	and	al,-2
Z	cmp	ax,_mbot_
Z	jb	brk_ov
Z	mov	bx,_STKLOW_
Z	cmp	bx,0
Z	jnz	abovestk
Z	cmp	ax,_sbot_
Z	jae	brk_ov
Z	mov	bx,sp
Z	cmp	ax,bx
Z	jae	brk_ov
Zbrk_ok2:
Z	mov	$MEMRY,ax	;new value is good so save it away
Z	sub	ax,ax
Z	pret
Z;heap is above stack
Zabovestk:
Z	cmp	ax,_mtop_
Z	ja	getstore
Z	cmp	ax,$MEMRY
Z	ja	brk_ok2
Z;			going to do a SETBLOCK call
Zgetstore:
Z	push	ax
Z	mov	bx,ax
Z	mov 	cx,4
Z	shr	bx,cl
Z	and	bx,0fffh
Z	add	bx,65		;bump to nearest 1k increment
Z	and	bx,0ffc0h	;and round
Z	push	bx
Z	push	es
Z	mov	cx,ds
Z	add	bx,cx		;get actual paragraph address
Z	mov	es,_PSP_
Z	sub	bx,_PSP_
Z	mov	ah,04ah
Z	int	21h		;SETBLOCK
Z	pop	es
Z	pop	bx
Z	pop	ax
Z	jc	brk_ov		; couldn't do it, so punt
Z	mov	$MEMRY,ax
Z	test	bx,0e000h
Z	jnz	brk_ov
Z	mov	cx,4
Z	shl	bx,cl
Z	mov	_mtop_,bx
Z	sub	ax,ax
Z	pret
Z; invalid request
Zbrk_ov:
Z	mov	errno_,-4
Z	mov	ax,-1
Z	test	ax,ax
Z	pret
Z	pend	brk
Z;
Z; rsvstk(size):		set safety margin for stack
Z;			this will make sure that at least size
Z;			bytes of stack below the current level remain.
Z;
Z	procdef	rsvstk,<<stksize,word>>
Z	mov	ax,sp
Z	sub	ax,stksize
Z	mov	_sbot_,ax
Z	pret
Z	pend	rsvstk
Z	finish
Z	end
STUNKYFLUFF
set `sum ssbrk.asm`
if test 58681 != $1
then
echo ssbrk.asm: Checksum error. Is: $1, should be: 58681.
fi
#
#
echo Extracting stdsave.c:
sed 's/^Z//' >stdsave.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <fcntl.h>
Z
Zint console;
Zint in,out;	/* old standard input and standard output */
Zstd_save()
Z{
Z	if (-1 == (console = open("con",O_RDWR)))
Z	{
Z		perror("sh : can't open console");
Z		return -1;
Z	}
Z	in = dup(0);
Z	out = dup(1);
Z	fdup(console,0);
Z	fdup(console,1);
Z	fdup(console,2);
Z	return 0;
Z}
Zvoid 
Zstd_restore()
Z{
Z	fdup(in,0);
Z	fdup(out,1);
Z	close(console);
Z	close(in);
Z	close(out);
Z}
STUNKYFLUFF
set `sum stdsave.c`
if test 61619 != $1
then
echo stdsave.c: Checksum error. Is: $1, should be: 61619.
fi
#
#
echo Extracting stksiz.c:
sed 's/^Z//' >stksiz.c <<\STUNKYFLUFF
Zint _STKSIZ = 8192/16;  /* (in paragraphs) */
Zint _HEAPSIZ = 32768/16; /* (in paragraphs) */
Zint _STKLOW = 1;		/* default is stack above heap (small only) */
STUNKYFLUFF
set `sum stksiz.c`
if test 11181 != $1
then
echo stksiz.c: Checksum error. Is: $1, should be: 11181.
fi
#
#
echo Extracting touch.c:
sed 's/^Z//' >touch.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z
Ztouch(argc,argv)
Z	char *argv[];
Z{
Z	while(--argc)
Z		utime(*(++argv),NULL);
Z}
STUNKYFLUFF
set `sum touch.c`
if test 31978 != $1
then
echo touch.c: Checksum error. Is: $1, should be: 31978.
fi
#
#
echo Extracting y.c:
sed 's/^Z//' >y.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <signal.h>
Z#include <setjmp.h>
Z
Z/* y and t
Z * y reads the standard input to standard output, then invokes cat
Z * to put one or more files to standard output
Z * tee copies standard input to output and puts a copy
Z * into a file specified on the command line
Z */
Z
Zvoid (*signal())();
Zvoid (*teesig)();
Zjmp_buf y_env;
Zstatic FILE *in,*out;
Z
ZFILE *fopen(),*fdopen();
Z
Zvoid y_intr()
Z{
Z	signal(SIGINT,SIG_IGN);
Z	longjmp(y_env,-1);
Z}
Z
Zy(argc,argv)
Z	int argc;
Z	char *argv[];
Z{
Z	register int c;
Z
Z	/* handle interrupts */
Z	if (-1 == setjmp(y_env))
Z	{
Z		static char *intmsg = "Interrupted\r\n";
Z		write(2,intmsg,strlen(intmsg));
Z		fclose(in);
Z		fclose(out);
Z		signal(SIGINT,teesig);
Z		return -1;
Z	}
Z
Z	/* set signal catcher */
Z	teesig = signal(SIGINT,y_intr);
Z
Z	if (NULL == (in = fdopen(0,"r")))
Z	{
Z		fprintf(stderr,"can't open stdin\n");
Z	};
Z	if (NULL == (out = fdopen(1,"w")))
Z	{
Z		fprintf(stderr,"can't open stdout\n");
Z	};
Z
Z	while(EOF != (c = agetc(in)))
Z		aputc(c,out);
Z	if (argc > 1)
Z		cat(argc,argv);
Z	fclose(in);
Z	fclose(out);
Z	signal(SIGINT,teesig);
Z	return 0;
Z}
Z
Zjmp_buf t_env;
Z
Zvoid t_intr()
Z{
Z	signal(SIGINT,SIG_IGN);
Z	longjmp(t_env,-1);
Z}
Z
Zt(argc,argv)
Z	int argc;
Z	char *argv[];
Z{
Z	register int c;
Z	register FILE *tfile;
Z	FILE *fopen();
Z
Z	/* handle interrupts */
Z	if (-1 == setjmp(t_env))
Z	{
Z		static char *intmsg = "Interrupted\r\n";
Z		write(2,intmsg,strlen(intmsg));
Z		fclose (tfile);
Z		fclose(in);
Z		fclose(out);
Z		signal(SIGINT,teesig);
Z		return -1;
Z	}
Z
Z	/* set signal catcher */
Z	teesig = signal(SIGINT,t_intr);
Z
Z	if (NULL == (tfile = fopen(*(++argv),"w")))
Z	{
Z		fprintf(stderr,"can't open %s\n",*argv);
Z	};
Z	if (NULL == (in = fdopen(0,"r")))
Z	{
Z		fprintf(stderr,"can't open stdin\n");
Z	};
Z	if (NULL == (out = fdopen(1,"w")))
Z	{
Z		fprintf(stderr,"can't open stdout\n");
Z	};
Z
Z	while(EOF != (c = agetc(in)))
Z	{
Z		aputc(c,out);
Z		if (tfile)
Z			aputc(c,tfile);
Z	}
Z	fclose(in);
Z	fclose(out);
Z	fclose(tfile);
Z	signal(SIGINT,teesig);
Z	return 0;
Z}
STUNKYFLUFF
set `sum y.c`
if test 44898 != $1
then
echo y.c: Checksum error. Is: $1, should be: 44898.
fi
echo ALL DONE BUNKY!
exit 0

-- 
                                                                          --
                .^.                        michael regoli 
                /|\        ...ihnp4!inuxc!iuvax!isrnix!mr 
               '|!|`                     <mr@isrnix.UUCP> 

jim@hp-pcd.UUCP (jim) (12/09/85)

        mr:

        The C-shell source posting for MSDOS has been truncated
        to 64K bytes in length on our system.  Could you please split
        the source into two or more pieces and repost it?


        Thanks a lot,


        Jim Andreas
        hp-pcd\!jim
        (503) 757-2000 x2860

        Portable Computer Division
        Hewlett-Packard
        1000 NE Circle Boulevard
        Corvallis, Oregon 97333

mr@isrnix.UUCP (michael regoli) (12/15/85)

#########################################################
#                                                       #
# This is a shell archive file.  To extract files:      #
#                                                       #
#    1)	Make a directory for the files.                 #
#    2) Write a file, such as "file.shar", containing   #
#       this archive file into the directory.           #
#    3) Type "sh file.shar".  Do not use csh.           #
#                                                       #
#########################################################
#
#
echo Extracting PACKING_LIST:
sed 's/^Z//' >PACKING_LIST <<\STUNKYFLUFF
Z
Z Volume in drive A is UNIX CSH PC
Z Directory of  A:\
Z
ZCMDS              794  10-03-85   1:56p
ZMAKEFILE         1879  10-03-85   2:22p
ZCSHELL   ARC    86912  11-12-85  11:13a
ZCTLBRK   ASM     1540  10-03-85   1:29p
ZFEXEC    ASM     1134   9-05-85   2:23p
ZSSBRK    ASM     1903   9-30-85   2:35p
ZCAT      C       1295   9-06-85  10:18a
ZCD       C        231   9-06-85   9:33a
ZCHMOD    C       2315   9-06-85   9:33a
ZCMDLIST  C       1058   9-12-85  11:02a
ZCMDS     C       2097   9-12-85  11:02a
ZCP       C       2184  10-03-85   2:46p
ZCRLF     C         59   9-06-85   9:33a
ZCROOT    C       3917   8-16-85   2:31p
ZDOPROG   C        180   9-06-85   9:33a
ZDRIVE    C        214   9-06-85   9:33a
ZDUMP2    C       6969   9-12-85  11:25a
ZECHO     C        193   9-06-85   9:33a
ZENV      C       2922   9-06-85   9:33a
ZFEXECV   C       1119   9-06-85   9:33a
ZFEXECVP  C       1017   9-06-85   9:33a
ZFGREP    C       2141   9-06-85  10:13a
ZGETCMD   C       1453   9-06-85   9:33a
ZINVALID  C        248   9-06-85   9:33a
ZLS       C       8042  10-03-85  12:27p
ZMAIN     C      11239  10-03-85   4:27p
ZMD       C        121   9-06-85   9:33a
ZMORE     C       4920   9-19-85  10:27a
ZMV       C       3802  10-03-85   2:43p
ZMVCP     C       2600  10-03-85   2:49p
ZPUSHD    C       1039   9-06-85   9:33a
ZPWD      C        268   9-06-85   9:33a
ZRM       C        537   9-06-85   9:33a
ZRMDIR    C        240   9-06-85   9:33a
ZSAVESTR  C        243   9-06-85   9:33a
ZSTDSAVE  C        438   9-06-85   9:33a
ZSTKSIZ   C        161  10-02-85   2:31p
ZTOUCH    C        104   9-06-85   9:33a
ZY        C       2093   9-06-85  10:17a
Z_CROOT   C       5537   9-19-85  10:16a
ZCSHELL   DOC    16714  10-03-85   2:11p
ZSHELL    EXE    28464  10-03-85   4:27p
ZSHELL    LNK      276  10-03-85   2:22p
ZMAIN     S       9750  10-03-85   9:24a
Z       44 File(s)    115712 bytes free
STUNKYFLUFF
set `sum PACKING_LIST`
if test 56252 != $1
then
echo PACKING_LIST: Checksum error. Is: $1, should be: 56252.
fi
#
#
echo Extracting _croot.c:
sed 's/^Z//' >_croot.c <<\STUNKYFLUFF
Z/* Copyright (C) 1981,1982, 1983 by Manx Software Systems */
Z#include <stdio.h>
Z#include <errno.h>
Z#include <fcntl.h>
Z#ifndef NULL
Z#define NULL ((void *)0)
Z#endif
Z
Zchar *get_first(), *get_next(), *sbrk();
Zchar *wilderr = "No wild cards in command names!!\r\n";
Z
Z/* noexpand can be set by routines before calling _Croot */
Zint noexpand = 0;
Z
Z#define ARGMAX 256
Zstatic char *Argv[ARGMAX];
Zstatic int Argc;
Zstatic char curr_path[128];
Z
Z_Croot(cp,cmd)
Zregister char *cp;
Zint (*cmd)();
Z{
Z	int returnval = 0;
Z	char *startbuf,*endbuf;
Z	register char *cp2;
Z	char *wild_match;
Z	char *index(),*rindex(), *save_str();
Z	char *path,*copy; int j;
Z	char *quote;
Z	int k,omode; char *fname;
Z	int in = -1, out = -1;
Z	/* lets try not to free things not allocated by malloc */
Z	startbuf = cp; endbuf = &cp[strlen(cp)];
Z	if (!noexpand)
Z	{
Z		/* ls is a special case !!! */
Z		if(0 == strncmp(cp,"ls",2) || 0 == strncmp(cp,"dir",3))
Z			noexpand++;
Z	}
Z
Z	/* loop through arguments */
Z	for (Argc = 0;;) 
Z	{
Z		/* skip blanks */
Z		while (*cp == ' ' || *cp == '\t')
Z			++cp;
Z
Z		/* if you're at the end of command line, you're done */
Z		if (*cp == 0)
Z			break;
Z		/* handle redirection . . . */
Z		if (*cp == '>')
Z		{
Z			k = 1;
Z			if (cp[1] == '>')
Z			{
Z				++cp;
Z				omode = O_CREAT | O_WRONLY | O_APPEND; 
Z			}
Z			else
Z				omode = O_CREAT | O_WRONLY | O_TRUNC;
Z			goto redirect;
Z		} else if (*cp == '<')
Z		{
Z			k = 0;
Z	redirect:
Z			while (*++cp == ' ' || *cp == '\t')
Z				;
Z			fname = cp;
Z			while(*++cp)
Z				if (*cp == ' ' || *cp == '\t')
Z				{
Z					*cp++ = 0;
Z					break;
Z				}
Z			close(k);
Z			if (k)
Z				out = k = open(fname,omode);
Z			else
Z				in = k = open(fname,O_RDONLY);
Z			if (k == -1)
Z			{
Z				perror("redirection");
Z				return -1;
Z			}
Z			/* go back for next argument */
Z			continue;
Z		}
Z		/* find beginning of next argument */
Z		cp2 = cp;	/* save original pointer to the string */
Z		while (*++cp2)
Z		{
Z			/* if you hit a space char - stick a null in to terminate last
Z			   argument
Z			 */
Z			if (*cp2 == ' ' || *cp2 == '\t') 
Z			{
Z				*cp2++ = 0;
Z				break;
Z			}
Z		}
Z
Z		/* if no wild card characters, do it the old fashioned way */
Z		if (index(cp,'*') == NULL && index(cp,'?') == NULL)
Z		{
Znotranslate:
Z			if (*cp == '\'')
Z			/* pass through untranslated, with quotes stripped */
Z			{
Z				cp++;	/* point past quote */
Z				if ( NULL == (quote = rindex(cp,'\'')))
Z				{
Z					write(2,"sh - no close quotes on command line\r\n",38);
Z					goto free_args;
Z				}
Z				*quote = '\0';
Z			}
Z			/* update the next argv pointer */
Z			Argv[Argc] = cp;
Z			/* bump the argument count */
Z			if (++Argc == ARGMAX)
Z				abort();
Z		}
Z		else
Z		{
Z			if (*cp == '"' || *cp == '\'')
Z				goto notranslate;
Z			if (noexpand)
Z				goto notranslate;
Z			/* wild cards not permitted on first run thru */
Z			if (Argc == 0)
Z			{
Z				write(2,wilderr,strlen(wilderr));
Z				return -1;
Z			}
Z			/* if there is a path included, save it off */
Z			if ((path = rindex(cp,'\\')) || (path = rindex(cp,'/')))
Z			{
Z				copy = cp;
Z				/* copy to curr_path, mapping / to \ */
Z				for (j = 0; j < sizeof(curr_path) && copy != path+1; copy++,j++)
Z					curr_path[j] = (*copy == '/' ? '\\' : *copy);
Z				/* terminate string */
Z				curr_path[j] = '\0';
Z			}
Z			else if (cp[1] == ':')
Z			{
Z				copy = cp;
Z				for (j = 0; j < 2; j++)
Z					curr_path[j] = *copy++;
Z				curr_path[j] = '\0';
Z			} else
Z			/* null path */
Z				curr_path[0] = 0;
Z			if (wild_match = get_first(cp))
Z			{
Z				/* update the next argv pointer */
Z				Argv[Argc]= save_str(wild_match);
Z				/* bump the argument count */
Z				if (++Argc == ARGMAX)
Z					abort();
Z				/* get the rest of the matching file names */
Z				while (wild_match = get_next())
Z				{
Z					
Z					/* update the next argv pointer */
Z					Argv[Argc] = save_str(wild_match);
Z					/* bump the argument count */
Z					if (++Argc == ARGMAX)
Z						abort();
Z				}
Z			}
Z		}
Z		cp = cp2;	/* point to beginning of next argument */
Z	}
Z	Argv[Argc] = NULL;	
Z	returnval=(*cmd)(Argc,Argv);
Z	if (in != -1)
Z		close(in);
Z	if (out != -1)
Z		close(out);
Z	/* free anything not dynamically allocated */
Zfree_args:
Z	if (!noexpand)
Z	{
Z		for(j = 1;j < Argc; j++)
Z			if (
Z			!(Argv[j] >= startbuf && Argv[j] <= endbuf)	/* not in cmd line */
Z			&& Argv[j]									/* Not NULL			*/
Z		   		)
Z			free(Argv[j]);
Z	}
Z	noexpand = 0;
Z	return returnval;
Z}
Z
Zchar *
Zsave_str(s)
Z	register char *s;
Z{
Z	register char *r,*malloc();
Z	int pathlen;
Z	/* squirrel away matched file name */
Z	if (NULL == (r = malloc(strlen(s)+(pathlen = strlen(curr_path))+1)))
Z		abort();
Z	strcat(curr_path,s);
Z	strcpy(r,curr_path);
Z	curr_path[pathlen] = '\0';
Z	return r;
Z}
Z
Zabort()
Z{
Z	write(2, "Too many args.", 14);
Z	_exit(200);
Z}
Z
Z
Ztypedef struct
Z{
Z	char dos_reserved[21];
Z	char attribute;
Z	unsigned file_time;
Z	unsigned file_date;
Z	long file_size;
Z	char file_name[13];
Z} fcb;
Zfcb wildcard;
Z
Zchar *get_first(fname)
Z	char *fname;
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&wildcard);
Z	result = bdos(0x4E,fname,0);
Z	/* make the find first call */
Z	if(2 == result || 18 == result)
Z		return NULL;
Z	return &(wildcard.file_name[0]);
Z}
Z
Zchar *get_next()
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&wildcard);
Z	result = bdos(0x4f,0,0);
Z	/* make the find next call */
Z	if (18 == result)
Z		return NULL;
Z	return &(wildcard.file_name[0]);
Z}
STUNKYFLUFF
set `sum _croot.c`
if test 54651 != $1
then
echo _croot.c: Checksum error. Is: $1, should be: 54651.
fi
#
#
echo Extracting cat.c:
sed 's/^Z//' >cat.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <signal.h>
Z#include <setjmp.h>
Z
Zvoid (*signal())();
Z
ZFILE *fd1,*fd2;
Z
Zvoid (*oldsig)();
Zchar *fgets();
Zjmp_buf catenv;
Zcatintr()
Z{
Z	signal(SIGINT,SIG_IGN);			/* ignore signals */
Z	fclose(fd1);
Z	fclose(fd2);
Z	signal(SIGINT,oldsig);			/* restore shell interrupt */
Z	longjmp(catenv,-1);
Z}
Z
Zcat(argc,argv)
Z	char *argv[];
Z{
Z	char *intmsg = "Interrupt received\n";
Z
Z	FILE *fdopen(), *fopen();
Z	if (-1==setjmp(catenv))
Z	{
Z		write(2,intmsg,strlen(intmsg));
Z		return -1;
Z	}
Z	oldsig = signal(SIGINT,catintr);	/* trap interrupts from keyboard */
Z	/* get standard output opened for business */
Z	if (NULL == (fd2 = fdopen(1,"w")))
Z	{
Z		perror("cat : Can't open stdout");
Z	}
Z
Z	/* handle pipes */
Z	if (argc == 1)
Z	{
Z		if (NULL == (fd1 = fdopen(0,"r")))
Z		{
Z			perror("cat : Can't open stdin");
Z		}
Z		_cat();
Z		fclose(fd1);fclose(fd2);
Z	}
Z	/* handle specified files */
Z	else
Z	{
Z		while(--argc)
Z		{
Z			if (NULL == (fd1 = fopen(*(++argv),"r")))
Z			{
Z				fprintf(stderr,"can't open %s\n",*argv);
Z				continue;
Z			}
Z			_cat();
Z			fclose(fd1);
Z		}
Z	}
Z	fclose(fd2);
Z	signal(SIGINT,oldsig);				/* restore old int catcher		*/
Z}
Z
Z_cat()
Z{
Z	char buffer[512];
Z	while (NULL != fgets(buffer,512,fd1))
Z		fputs(buffer,fd2);
Z}
STUNKYFLUFF
set `sum cat.c`
if test 12297 != $1
then
echo cat.c: Checksum error. Is: $1, should be: 12297.
fi
#
#
echo Extracting cd.c:
sed 's/^Z//' >cd.c <<\STUNKYFLUFF
Z#include <stdio.h>
Zcd(argc,argv)
Zchar *argv[];
Z{
Z	static char *usage = "usage : cd newdir";
Z	if (argc == 1)
Z		write(2,usage,strlen(usage));
Z	if (-1 == chdir(*(++argv)))
Z	{
Z		perror("cd");
Z		return -1;
Z	}
Z	return 0;
Z}
STUNKYFLUFF
set `sum cd.c`
if test 24433 != $1
then
echo cd.c: Checksum error. Is: $1, should be: 24433.
fi
#
#
echo Extracting chmod.c:
sed 's/^Z//' >chmod.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <stat.h>
Z#ifdef MAIN
Zmain
Z#else
Zch_mod
Z#endif
Z(argc,argv)
Z	char *argv[];
Z{
Z	int or_mask,and_mask,file_stat;
Z	struct { int ax,bx,cx,dx,si,di,ds,es; } regs;
Z	extern int _dsval;
Z	char *current;
Z	regs.ds = _dsval;
Z	if (argc==1)
Z	{
Z		fprintf(stderr,"Usage : chmod +|-[ahw] file [file ...]\n");
Z	}
Z	/* set attributes to default */
Z	or_mask = 0; and_mask = 0xFFFF;
Z	while(--argc)
Z	{
Z		current = *(++argv);
Z		switch (*current)
Z		{
Z		case '-':
Z			while (*++current)
Z			{
Z				switch(*current)
Z				{
Z				case 'w':
Z				case 'W':
Z					or_mask |= ST_RDONLY;
Z					break;
Z				case 'h':
Z				case 'H':
Z					and_mask &= (ST_HIDDEN ^ 0xFFFF);
Z					break;
Z				case 'r':
Z				case 'R':
Z					or_mask |= ST_HIDDEN;
Z					break;
Z				case 'a':
Z				case 'A':
Z					and_mask &= (ST_ARCHIV ^ 0xFFFF);
Z					break;
Z				case 's':
Z				case 'S':
Z					and_mask &= (ST_SYSTEM ^ 0xFFFF);
Z					break;
Z				default:
Z					write(2,"invalid attribute\r\n",19);
Z				return -1;
Z				}
Z			}
Z			break;
Z		case '+':
Z			while(*++current)
Z			{
Z				switch(*current)
Z				{
Z				case 'w':
Z				case 'W':
Z					and_mask &= (ST_RDONLY ^ 0xFFFF);
Z					break;
Z				case 'h':
Z				case 'H':
Z					or_mask |= ST_HIDDEN;
Z					break;
Z				case 's':
Z				case 'S':
Z					or_mask |= ST_SYSTEM;
Z					break;
Z				case 'r':
Z				case 'R':
Z					and_mask &= (ST_HIDDEN ^ 0xFFFF);
Z					break;
Z				case 'a':
Z				case 'A':
Z					or_mask |= ST_ARCHIV;
Z					break;
Z				default:
Z					write(2,"invalid attribute\r\n",19);
Z					return -1;
Z
Z				}
Z			}
Z			break;
Z		default:
Z			/* get current attribute */
Z			regs.ax = 0x4300;
Z			regs.dx = (int)current;
Z			regs.ds = _dsval;
Z			sysint(0x21,&regs,&regs);
Z			file_stat = regs.cx;
Z			fprintf(stderr,"current attribute for %s = %x\n",
Z				current,file_stat);
Z			/* set new attribute */
Z			file_stat |= or_mask;
Z			file_stat &= and_mask;
Z			regs.ax = 0x4301;
Z			regs.dx = (int)current;
Z			regs.cx = file_stat;
Z			regs.ds = _dsval;
Z			sysint(0x21,&regs,&regs);
Z			/* get attribute to see if it changed */
Z			regs.ax = 0x4300;
Z			regs.dx = (int)current;
Z			regs.ds = _dsval;
Z			sysint(0x21,&regs,&regs);
Z			file_stat = regs.cx;
Z			fprintf(stderr,"new attribute for %s = %x\n",
Z				current,file_stat);
Z			break;
Z		}
Z	}
Z}
Z
STUNKYFLUFF
set `sum chmod.c`
if test 37605 != $1
then
echo chmod.c: Checksum error. Is: $1, should be: 37605.
fi
#
#
echo Extracting cmdlist.c:
sed 's/^Z//' >cmdlist.c <<\STUNKYFLUFF
Z
Zextern int cmds(),ls(), cp(), rm(), do_prog(),pushd(),popd(),drive(), ver(),
Z		more(),fgrep(),scr_clear(),set(),ch_mod(),cat(),echo(), 
Z		y(),t(),dump(),
Z		last(),invalid(),mv(),md(),touch(),cd(),pwd(),rd(),hist(),my_exit();
Ztypedef struct
Z{
Z	char *cmdname;
Z	int (*func)();
Z} builtin;
Zbuiltin commands[] =
Z{
Z	"a:",drive,
Z	"b:",drive,
Z	"c:",drive,
Z	"cat",cat,
Z	"cd",cd,
Z	"chdir",cd,
Z	"chmod",ch_mod,
Z	"cls",scr_clear,
Z	"commands",cmds,
Z	"copy",cp,
Z	"cp",cp,
Z	"copy",cp,
Z	"d:",drive,
Z	"del",rm,
Z	"dir",ls,
Z	"dump",dump,
Z	"e:",drive,
Z	"echo",echo,
Z	"era",rm,
Z	"erase",rm,
Z	"error",last,
Z	"exit",my_exit,
Z	"f:",drive,
Z	"fgrep",fgrep,
Z	"g:",drive,
Z	"h:",drive,
Z	"hd",dump,
Z	"hist",hist,
Z	"history",hist,
Z	"i:",drive,
Z	"j:",drive,
Z	"ls",ls,
Z	"md",md,
Z	"mkdir",md,
Z	"more",more,
Z	"mv",mv,
Z	"no history",invalid,
Z	"popd",popd,
Z	"pushd",pushd,
Z	"pwd",pwd,
Z	"rd",rd,
Z	"rm",rm,
Z	"rmdir",rd,
Z	"set",set,
Z	"tee",t,
Z	"touch",touch,
Z	"version",ver,
Z	"y",y
Z};
Zint numcmds =  (sizeof(commands)/sizeof(builtin));
STUNKYFLUFF
set `sum cmdlist.c`
if test 20023 != $1
then
echo cmdlist.c: Checksum error. Is: $1, should be: 20023.
fi
#
#
echo Extracting cmds:
sed 's/^Z//' >cmds <<\STUNKYFLUFF
Za:              b:              c:              cat             
Zcd              chdir           chmod           cls             
Zcommands        copy            cp              copy            
Zd:              del             dir             dump            
Ze:              echo            era             erase           
Zerror           exit            f:              fgrep           
Zg:              h:              hd              hist            
Zhistory         i:              j:              ls              
Zmd              mkdir           more            mv              
Zno history      popd            pushd           pwd             
Zrd              rm              rmdir           set             
Ztee             touch           version         y               
Z
STUNKYFLUFF
set `sum cmds`
if test 18223 != $1
then
echo cmds: Checksum error. Is: $1, should be: 18223.
fi
#
#
echo Extracting cmds.c:
sed 's/^Z//' >cmds.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <fcntl.h>
Ztypedef struct
Z{
Z	char *cmdname;
Z	int (*func)();
Z} builtin;
Z
Zchar *str_lower();
Z
Zextern int result;
Z
Zextern int cmds(),ls(), cp(), rm(), do_prog(),pushd(),popd(),drive(), ver(),
Z		more(),fgrep(),scr_clear(),set(),ch_mod(),cat(),echo(), 
Z		y(),t(),dump(),
Z		last(),invalid(),mv(),md(),touch(),cd(),pwd(),rd(),hist(),my_exit();
Z
Zmy_exit(argc,argv)
Z	char *argv[];
Z{
Z	exit(result);
Z}
Z
Zver()
Z{
Z	extern char *version;
Z	write(2,version,strlen(version));
Z	write(2,"\r\n",2);
Z}
Z
Zextern builtin commands[];
Zextern int numcmds;
Zchar *histerr = "no history";
Zint j,hiscount;
Zchar *history[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
Z					 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
Z
Zint histsize = (sizeof(history)/sizeof(char *));
Z
Zcmds()
Z{
Z	char *current;
Z	register int i,j,col;
Z	col = 1;
Z	for (i = 0; i < numcmds; i++)
Z	{
Z		current = commands[i].cmdname;
Z		write(1,current,j = strlen(current));
Z		for (;j < 16;j++)
Z			write(1," ",1);
Z		if (col == 4)
Z		{
Z			col = 1;
Z			crlf();
Z		}
Z		else
Z			++col;
Z	}
Z	crlf();
Z}
Z
Zfindcmd(cmdbuf)
Z	char *cmdbuf;
Z{
Z	register int low,high,mid;
Z	char localbuf[256];
Z	int cond;
Z	strcpy(localbuf,cmdbuf);
Z	cmdbuf = str_lower(localbuf);
Z	low = 0;
Z	high = numcmds - 1;
Z	while (low <= high)
Z	{
Z		mid = (low+high) / 2;
Z		if	( ( cond =  strncmp( cmdbuf,
Z								 commands[mid].cmdname,
Z								 strlen(commands[mid].cmdname) ) ) < 0 )
Z				high = mid - 1;
Z		else if (cond > 0)
Z				low = mid + 1;
Z		else
Z		{
Z			/* kludge to allow for program invocations like d:command */
Z			if (cmdbuf[1] == ':')
Z				if (cmdbuf[2] == '\0')
Z					return mid;
Z				else
Z					return -1;
Z			return mid;
Z		}
Z	}
Z	return -1;
Z}
Z
Zhist()
Z{
Z	register int i;
Z	char localbuf[256];
Z	if (j < histsize)
Z		i = 0;
Z	else
Z		i = j - histsize + 1;
Z	for (;i <= j; i++)
Z	{
Z		sprintf(localbuf,"%d : %s\r\n",i,history[i % histsize]);
Z		write(1,localbuf,strlen(localbuf));
Z	}
Z}
Z
Zlast()
Z{
Z	printf("return code of last command %d\n",result);
Z	return result;
Z}
STUNKYFLUFF
set `sum cmds.c`
if test 01085 != $1
then
echo cmds.c: Checksum error. Is: $1, should be: 01085.
fi
#
#
echo Extracting cp.c:
sed 's/^Z//' >cp.c <<\STUNKYFLUFF
Z#include <stdio.h>
Zchar *me;
Z/* cp.c - implements a version of UNIX cp */
Zchar target_name[128];
Z#ifndef MAIN
Zcp
Z#else
Zmain
Z#endif
Z(argc,argv)
Zint argc;
Zregister char *argv[];
Z{
Z	static char *usage = "cp : usage cp file1 [file2 . . fileN] target\r\n";
Z    char target[128],*fname_part();
Z    register int i;
Z	me = argv[0];
Z    if (argc < 3)
Z    {
Z	write(2,usage,strlen(usage));
Z	return(-1);
Z    }
Z    strcpy(target, argv[argc-1]);
Z    /* kill trailing backslashes */
Z    if (target[i = strlen(target) - 1] == '\\')
Z	target[i] = '\0';
Z    if (argc == 3)
Z    {
Z	if (target[1] == ':' && !target[2])
Z	    strcat(target,fname_part(argv[1]));
Z	/* if the target doesn't exist and it's not a directory then rename */
Z	if (access(target,0) && !dirp(target))
Z	{
Z	    fprintf(stderr,"copying %s to %s\n",argv[1],target);
Z	    filecopy(target,argv[1]);
Z	}
Z	else
Z	{
Z	    /* if the target is a directory copy to same name that directory */
Z	    if (dirp(target))
Z	    {
Z		int len;
Z		strcpy(target_name,target);
Z		if (target_name[(len = strlen(target_name))-1] != '\\')
Z		{
Z		    target_name[len = strlen(target_name)] = '\\';
Z		    target_name[len+1] = '\0';
Z		}
Z		strcat(target_name,fname_part(argv[1]));
Z		fprintf(stderr,"copying %s to %s\n",argv[1],target_name);
Z		filecopy(target_name,argv[1]);
Z	    }
Z	    else
Z	    {
Z		fprintf(stderr,"copying %s to %s\n",argv[1],target);
Z		filecopy(target,argv[1]);
Z	    }
Z	}
Z	return(0);
Z    }
Z    /* handle special case of a drive designation */
Z    if (target[(i = strlen(target))-1] != ':')
Z	if (!dirp(target))
Z	{
Z	    fprintf(stderr,"cp : %s isn't a directory\n",target);
Z	    return(-1);
Z	}
Z    for (i = 1; i < argc-1; i++)
Z    {
Z	int len;
Z	strcpy(target_name,target);
Z	if (target_name[(len = strlen(target_name))-1] != '\\')
Z	{
Z	    target_name[len = strlen(target_name)] = '\\';
Z	    target_name[len+1] = '\0';
Z	}
Z	strcat(target_name,fname_part(argv[i]));
Z	if (!filep(argv[i]))
Z	{
Z	    fprintf(stderr,"cp : %s isn't a file\n",argv[i]);
Z	    continue;
Z	}
Z	fprintf(stderr,"copying %s to %s\n",argv[i],target_name);
Z	filecopy(target_name,argv[i]);
Z    }
Z	return 0;
Z}
STUNKYFLUFF
set `sum cp.c`
if test 27151 != $1
then
echo cp.c: Checksum error. Is: $1, should be: 27151.
fi
#
#
echo Extracting crlf.c:
sed 's/^Z//' >crlf.c <<\STUNKYFLUFF
Zcrlf()
Z{
Z	static char *cr = "\r\n";
Z	write(1,cr,2);
Z}
STUNKYFLUFF
set `sum crlf.c`
if test 19813 != $1
then
echo crlf.c: Checksum error. Is: $1, should be: 19813.
fi
#
#
echo Extracting croot.c:
sed 's/^Z//' >croot.c <<\STUNKYFLUFF
Z/* Copyright (C) 1981,1982, 1983 by Manx Software Systems */
Z#include <errno.h>
Z#include <fcntl.h>
Z#ifndef NULL
Z#define NULL ((void *)0)
Z#endif
Z
Zchar *get_first(), *get_next(), *sbrk();
Z
Z#define ARGMAX 256
Zstatic char *Argv[ARGMAX];
Zstatic int argvsize;
Zstatic int Argc;
Zstatic char curr_path[128];
Z
Znoper()
Z{
Z	return 0;
Z}
Z
Zint (*cls_)() = noper;
Zextern char _ioflg[];
Z
ZCroot(cp, first)
Zregister char *cp;
Z{
Z	register char *cp2;
Z	char *save;
Z	char *wild_match;
Z	char *index(),*rindex(), *save_str();
Z	char *path,*copy; int j;
Z
Z	_ioflg[0] = isatty(0);	/* set flag for i/o routines */
Z	_ioflg[1] = isatty(1);	/* set flag for i/o routines */
Z	_ioflg[2] = isatty(2);	/* set flag for i/o routines */
Z
Z
Z	/* Null out first argument */
Z	Argv[0] = "";
Z	Argc = first;
Z
Z	/* loop through arguments */
Z	for (;;) 
Z	{
Z		/* skip blanks */
Z		while (*cp == ' ' || *cp == '\t')
Z			++cp;
Z
Z		/* if you're at the end of command line, you're done */
Z		if (*cp == 0)
Z			break;
Z
Z		/* find beginning of next argument */
Z		cp2 = cp;	/* save original pointer to the string */
Z		*cp2 = (*cp2 == '/' ? '\\' : *cp2);
Z		while (*++cp2)
Z		{
Z			/* if you hit a space char - stick a null in to terminate last
Z			   argument
Z			 */
Z			if (*cp2 == ' ' || *cp2 == '\t') 
Z			{
Z				*cp2++ = 0;
Z				break;
Z			}
Z			*cp2 = (*cp2 == '/' ? '\\' : *cp2);
Z		}
Z
Z		/* if no wild card characters, do it the old fashioned way */
Z		if (index(cp,'*') == NULL && index(cp,'?') == NULL)
Z		{
Z			/* update the next argv pointer */
Z			Argv[Argc] = cp;
Z			/* bump the argument count */
Z			if (++Argc == ARGMAX)
Z				abort();
Z		}
Z		else
Z		{
Z			/* if there is a path included, save it off */
Z			if ((path = rindex(cp,'\\')) || (path = rindex(cp,'/')))
Z			{
Z				copy = cp;
Z				/* copy to curr_path, mapping / to \ */
Z				for (j = 0; j < sizeof(curr_path) && copy != path+1; copy++,j++)
Z					curr_path[j] = (*copy == '/' ? '\\' : *copy);
Z				/* terminate string */
Z				curr_path[j] = '\0';
Z			}
Z			else if (cp[1] == ':')
Z			{
Z				copy = cp;
Z				for (j = 0; j < 2; j++)
Z					curr_path[j] = *copy++;
Z				curr_path[j] = '\0';
Z			} else
Z			/* null path */
Z				curr_path[0] = 0;
Z			if (wild_match = get_first(cp))
Z			{
Z				/* update the next argv pointer */
Z				Argv[Argc]= save_str(wild_match);
Z				/* bump the argument count */
Z				if (++Argc == ARGMAX)
Z					abort();
Z				/* get the rest of the matching file names */
Z				while (wild_match = get_next())
Z				{
Z					
Z					/* update the next argv pointer */
Z					Argv[Argc] = save_str(wild_match);
Z					/* bump the argument count */
Z					if (++Argc == ARGMAX)
Z						abort();
Z				}
Z			}
Z		}
Z		cp = cp2;	/* point to beginning of next argument */
Z	}
Z	Argv[Argc] = NULL;	
Z	main(Argc,Argv);
Z	exit(0);
Z}
Z
Zchar *save_str(s)
Z	register char *s;
Z{
Z	register char *r;
Z	int pathlen;
Z	/* squirrel away matched file name */
Z	if (NULL == (r = sbrk(strlen(s)+(pathlen = strlen(curr_path))+1)))
Z		abort();
Z	strcat(curr_path,s);
Z	strcpy(r,curr_path);
Z	curr_path[pathlen] = '\0';
Z	return r;
Z}
Z
Zabort()
Z{
Z	write(2, "Too many args.", 14);
Z	_exit(200);
Z}
Z
Zexit(code)
Z{
Z	(*cls_)();
Z	_exit(code);
Z}
Z
Ztypedef struct
Z{
Z	char dos_reserved[21];
Z	char attribute;
Z	unsigned file_time;
Z	unsigned file_date;
Z	long file_size;
Z	char file_name[13];
Z} fcb;
Zfcb wildcard;
Z
Zchar *get_first(fname)
Z	char *fname;
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&wildcard);
Z	result = bdos(0x4E,fname,0);
Z	/* make the find first call */
Z	if(2 == result || 18 == result)
Z		return NULL;
Z	return &(wildcard.file_name[0]);
Z}
Z
Zchar *get_next(fname)
Z	char *fname;
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&wildcard);
Z	result = bdos(0x4f,0,0);
Z	/* make the find next call */
Z	if (18 == result)
Z		return NULL;
Z	return &(wildcard.file_name[0]);
Z}
STUNKYFLUFF
set `sum croot.c`
if test 02895 != $1
then
echo croot.c: Checksum error. Is: $1, should be: 02895.
fi
#
#
echo Extracting cshell.doc:
sed 's/^Z//' >cshell.doc <<\STUNKYFLUFF
Z        shell.exe 
Z
Z        command processor for ms.dos 
Z
Z        This is  yet  another  command.com  replacement.    It implements
Z        unix-like shell commands (ls, mv, fgrep, rm, chdir,  chmod)  etc.
Z        Other features include: 
Z
Z        1. Command line expansion of ambiguous file names.  
Z
Z        Programs  invoked  from  shell   never see '*.*' as an argument -
Z        they see  the  list  of  all  matching  files  in    the  current
Z        directory.  
Z
Z        2. History substitution - ala C-shell.  
Z
Z        History substitution is a powerful means to save retyping of long 
Z        command lines.It allows you to do things like re-execute the last 
Z        command,  re-execute  the  last  command but redirect output to a
Z        file, or execute a  new  command  with  arguments  from  previous
Z        command  lines.    The  last  20  commands  are saved, and can be
Z        reviewed by typing the 'history' command.  
Z
Z        Previous commands can be referred to by their number, or relative 
Z        to the  current  command's  number.    Parameters  from  previous
Z        commands can be seperated out and used individually.  
Z
Z        History  substitutions  specifications  come  in  two parts - the
Z        command number  specifier and the argument  specifier,  seperated
Z        by a  colon.    The  argument    specifier  is optional; if it is
Z        omitted, the entire command line is specified.  
Z
Z            <command specifier> ::= !! | !n | !-n 
Z            !!  = last command
Z            !n  = nth command
Z            !-n = command n commands before current command number
Z
Z            <argument specifier> ::= :[^$*] | :n | :n* | <empty>
Z            n   = number of argument (0 being the command name)
Z            ^   = first argument (i.e. argv[1])
Z            $   = last argument
Z            *   = ^-$, or nothing if only one word on command line
Z            n*  = arguments n through $
Z
Z            <history subst specification> ::= <command specifier><argument specifier>
Z
Z        This is not as complicatated as  it  may  appear.    Here  is  an
Z        example session.  
Z
Z            0% ls *.c
Z            *.c
Z            foo.c bar.c
Z            1% more foo.c
Z            /* edit the last argument of the last command */
Z            2% edit !!:$			
Z            /* go off and edit */
Z            /* reference last argument of last command */
Z            3% fgrep foo !!:$ bar.c 
Z            FOO.C : foo
Z            BAR.C : foo
Z            /* edit the second thru the last args of command 3 */
Z
Z
Z
Z
Z
Z
Z
Z
Z            4% edit !3:2*			
Z            (go off and edit)
Z            /* repeat last command */
Z            %5 !!
Z            (go off and edit)
Z            /* remove the 1st argument of the command 2 before the current one */
Z            %6 rm !-6:^
Z
Z        History  substitution here is a compatible subset of the C-shells
Z        history substitution  facility.    Cshell  allows  even   weirder
Z        combinations.  
Z
Z        3.  Multiple  commands  on  one  command line - Command lines are
Z        split at  semicolons.  
Z
Z        example 
Z
Z            %0 ls -l *.c ; make shell.exe ; exit
Z
Z        4. Character escapes and argument quoting - i.e.  '\;' suppresses 
Z        the command  parser  from  seeing  the  semicolon  as  a  command
Z        seperator.  
Z
Z        Quotes are handles thusly: 
Z
Z            1.  String  surrounded  by  single quotes are stripped of the
Z            single quotes, and passed without wild-card expansion to  the
Z            invoked program.  
Z
Z            2.  Strings  surrounded  by double quotes are passed complete
Z            with quotes to the calling program.   This  was  done  for  a
Z            version  of grep that I have that accepts regular expressions
Z            with embedded blanks within double quotes.  
Z
Z        5. Many builtin commands.  
Z
Z            Output of the 'commands' command
Z
Z            a:              b:              c:              cat             
Z            cd              chdir           chmod           cls             
Z            commands        copy            cp              copy            
Z            d:              del             dir             dump            
Z            e:              echo            era             erase           
Z            error           exit            f:              fgrep           
Z            g:              h:              hd              hist            
Z            history         i:              j:              ls              
Z            md              mkdir           more            mv              
Z            no history      popd            pushd           pwd             
Z            rd              rm              rmdir           set             
Z            tee             touch           version         y               
Z            
Z
Z        There are many that are simply aliases, e.g.    'copy'  and  'cp'
Z        invoke the same program.  
Z
Z        6. commands description syntax 
Z
Z            terms used in syntax explanations :
Z
Z
Z
Z
Z
Z
Z
Z
Z            
Z            fname ::= PC-DOS ambiguous or unambiguous file or directory name.
Z            
Z            uname ::= unambiguous PC-DOS file or directory name
Z            
Z            string ::= any string of printable characters of arbitrary(<512) length.
Z            
Z            filelist ::= filename [filename .. filename]
Z            
Z            noargs ::= no arguments at all
Z            
Z            {arg} ::= term is optional
Z            
Z            envstring ::=	<string>=<string> 
Z
Z        7. command syntax 
Z
Z        drive
Z            a: | b: | c: | d: | e: | f: | g: | h: | i: | j: <noargs> 
Z
Z            changes default  drive.    If  you  don't  have such a drive,
Z            nothing happens.  
Z
Z        cat
Z            cat {<filelist>} 
Z
Z            copies specified files to  standard  output.    If  none  are
Z            given, copies standard input to standard output 
Z
Z        cp
Z            cp | copy <filelist> <uname> 
Z
Z            copies specified  files  to  destination  file or device.  If
Z            more than one file is in the file list,  <uname>  must  be  a
Z            directory.  
Z
Z        cd
Z            cd | chdir <dirname> 
Z
Z            makes <dirname> the current default directory.  
Z
Z        chmod
Z            chmod {-|+[rwh]*} <filelist> 
Z
Z            change file permissions for specified files 
Z
Z            +r, -r turn on or off read permission - i.e. hide the file.
Z            +w, -w turn on or off write permission.
Z            +h, -h turn on or off hidden attribute - converse of r
Z            +a, -a turn on or off archive attribute
Z
Z            Note  that '-r' or '+rwh' are both valid syntax for switches.
Z            Also new permission switches  are  permissable  between  file
Z            names  with  the  following  warning: I don't reset the masks
Z            between file names - if you have a second batch of  attribute
Z            changes on  the  command  line,  the  effect is additive.  If
Z            you're not  careful,  you  could  make  a  mess  of  a  files
Z            attributes.  
Z
Z
Z
Z
Z
Z
Z
Z
Z            If  you don't specify any attribute switches, file attributes
Z            will be set  to  0,  which  means  read,write,not  hidden,not
Z            system, not  modified since last backup.  
Z
Z        cls
Z            cls | clear <noargs> 
Z
Z            clears the screen and homes the cursor.  
Z
Z        commands
Z            commands <noargs> 
Z
Z            prints a table of available built-in commands.  
Z
Z        del
Z            del 
Z
Z            synonym for rm.  
Z
Z        dir
Z            dir 
Z
Z            synonym for ls.  
Z
Z        dump
Z
Z            dump filespec [block [page]] | [segment:[offset]] [count] 
Z
Z            Where a block is 64K bytes and a page is 256 bytes
Z            Segment:offset are standard 8086 notation in hexadecimal
Z            Count is the number of bytes to dump in decimal
Z
Z            This came from some anonymous public domain source, ported by me
Z
Z        echo
Z            echo <anything> 
Z
Z            echos argument list to screen.  
Z
Z        era
Z            era 
Z
Z            synonym for rm.  
Z
Z        error
Z            error <noargs> 
Z
Z            prints returned value of last command to the screen.  
Z
Z        exit
Z            exit <noargs> 
Z
Z            terminates execution of the shell.  
Z
Z        fgrep
Z            fgrep <pattern> <filelist> 
Z
Z            looks for unambiguous pattern <pattern> in <filelist>.  echos 
Z
Z
Z
Z
Z
Z
Z
Z
Z            lines matching to the screen.  
Z
Z        hist
Z            hist | history <noargs> 
Z
Z            prints history list to standard output.  
Z
Z        ls
Z            ls | dir {-[alqcr]} <filelist> 
Z
Z            Lists files that match <filelist> 
Z
Z            -a all  files,  including  system  files are listed.  '.' and
Z            '..' are suppressed, but you know they're there if  you  need
Z            them, don't you?  
Z            -l prints out file times, permissions, etc 
Z            -q suppresses header line from display - useful when you want 
Z            to pipe stuff into another program.  
Z            -c print as one column.  
Z            -r recurse through all encountered subdirectories.  
Z
Z        md
Z            md | mkdir <uname> 
Z
Z            make a directory.  Prints an error if it can't be done 
Z
Z        more
Z            more {-[0-9]*} {<filelist>} 
Z
Z            List file to screen with pauses 
Z
Z            -n  specify  tab  width  when  expanding  tabs, where n is an
Z            integer.  more acts like 'cat'  when  redirected  -  you  can
Z            concatenate files  in this manner.  If no files are specifed,
Z            standard input is 'mored.' 
Z
Z        mv
Z            mv <filelist> <uname> 
Z
Z            moves specified file or files to target specifed by  <uname>.
Z            If  there  is  more  than one file in list, <uname> must be a
Z            directory 
Z
Z        popd
Z            popd <noargs> 
Z
Z            returns to directory at top of directory stack.  
Z
Z        pushd
Z            pushd <uname> 
Z
Z            save  current  working  directory  on  directory  stack,  and
Z            changes current working directory to <uname>.  
Z
Z        pwd
Z            pwd 
Z
Z            prints current working directory to standard output.  
Z
Z
Z
Z
Z
Z
Z
Z
Z        rd
Z            rd | rmdir <uname> 
Z
Z            remove specified directory if possible.  
Z
Z        rm
Z            rm {-q} <filelist> 
Z
Z            blows  away all files in <filelist>. If -q is specified, will
Z            ask if they should be removed.  
Z
Z        set
Z            set {<envstring> {<envstring> .. <envstring>}} 
Z
Z            sets a string in the environment.   If  you  specify  'name='
Z            with   no   string   after,   it  will  remove  it  from  the
Z            environment.  If you don't specify a string, set  prints  out
Z            current environment.  
Z
Z        tee
Z            tee <uname> 
Z
Z            Copies  standard  input to standard output, depositing a copy
Z            in <uname> 
Z
Z        touch
Z            touch <filelist> 
Z
Z            Makes the modification time of specified  files  the  current
Z            date and time.  
Z
Z        y
Z            y <filelist> 
Z
Z            copies standard input to standard output, and then copies the 
Z            specified files  to standard output.  Sort of the opposite of
Z            tee, in other words.  
Z
Z        7. Helpful hints 
Z
Z            Use forward slashes in all path names - they get converted to 
Z            back slashes before  dos  hears  about  them.    If  you  are
Z            invoking a program that expects forward slashes (dos external 
Z            commands frinstance) precede it with a back slash.  
Z
Z            put  single  quotes around arguments with semicolons in them,
Z            so they don't turn into command delimiters.  
Z
Z            The set command affects only the local  shell's  environment.
Z            You can 'exit' to command.com and the original environment is 
Z            intact.   The  local  environment  is  4K  large  -  which is
Z            useful.  
Z
Z            Exit and re-invoke if you have trouble loading large programs 
Z            from it - shell dynamically allocates and  frees  memory  all
Z            the  time,  but the AZTEC run-time doesn't tell DOS to shrink
Z            memory 
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z        8. Implementation notes 
Z
Z            DOS doesn't acknowledge  a  'change  default  drive'  command
Z            until you issue  a  'get  current directory' call.  Why?  The
Z            only way I figured this out is by disassembling command.com.  
Z
Z            This was developed with AZTEC C by MANX.  In  it  are  a  few
Z            hacked  up  pieces  of  AZTECS library source, which I hereby
Z            acknowledge.  If MANX has  a  problem  with  me  distributing
Z            them,  they  can  call  me direct - I figure I'm doing them a
Z            favor by disseminating this program  as  an  example  of  the
Z            power and quality of their compiler and development tools.  
Z
Z            If  you  have  the AZTEC compiler and MANX's version of make,
Z            you can recreate the shell  from  source,  by  using  arc  to
Z            unpack everything into a directory, editing the macros BINDIR 
Z            and CLIB  and then making  shell.com.  I wouldn't try it with
Z            any other compiler, because I make a lot of  calls  to  AZTEC
Z            specific routines.    You can write your own commands and add
Z            them  by  editing  cmds.c,  and  putting  the  name  of  your
Z            subroutine and its associated command string into the builtin 
Z            array.   
Z
Z            You  can  safely  modify  any  of my builtins, as long as you
Z            don't assume that all of your static variables are  going  to
Z            stay initialized to startup values.  
Z
Z            Any  of the other code (main.c, fexecvp.c fexecv.c) modify at
Z            your own peril.  I break them every time I do it, and I wrote 
Z            them!!!  
Z
Z            PC|MS-DOS has a limit of 20 file  handles.    If  you  add  a
Z            command  that opens files, make sure you catch the ctrl-break
Z            signal and close them.  Look at CAT.C or Y.C for examples.  
Z
Z        9. BUGS 
Z            External DOS commands have trouble parsing the  command  line
Z            when invoked from   shell.  The command line gets garbled.  I
Z            spent a lot of time trying to figure this problem out  to  no
Z            avail.  They apparently get their command line arguments some 
Z            way that  is a mystery to me.  The only solution is either to
Z            either run command.com, or 'exit'  to  the  original  command
Z            prompt.  
Z
Z            This  problem  has  kept  me  from running this as a straight
Z            command.com   replacement.    It  just  goes  to  show   that
Z            Microsoft and IBM have one hell of a time following their own 
Z            rules.  
Z
Z            Programs  compiled  by  AZTEC  C  that don't set up their own
Z            signal  handlers  seem  to  be  'unbreakable'  -  you   can't
Z            ctrl-break  out  of  them, as though SIGINT is set to SIG_IGN
Z            before entry.  You might not want to invoke such a program if 
Z            it lasts hours and you want to be able to break  out  of  it.
Z            FIXED in current version.  Thanks to AZTEC Tech Support.  
Z
Z        10. HISTORY 
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z            V 1.0 - Initial release
Z
Z            Functional, but   somewhat   buggy.     Lacked  full  history
Z            substitution.  
Z
Z            V 1.1
Z
Z            Added history substitution.  Fixed some bugs.  This has  been
Z            floating around for a while.  
Z
Z            V 1.2
Z
Z            Fixed bugs.    Added 'free space' display to ls -l. Minimized
Z            weird behavior of cp and  mv.    Did  you  know  that  PC-DOS
Z            doesn't  think  the  root directory is a directory if you ask
Z        it?  Caused much pain.  
Z
Z        QUESTIONS COMMENTS GOTO 
Z        KENT WILLIAMS
Z        NORAND INC.
Z        550 2nd ST. S.E.
Z        Cedar Rapids Iowa 52401
Z        (319) 338-6053 (HOME VOICE)
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
Z
STUNKYFLUFF
set `sum cshell.doc`
if test 25555 != $1
then
echo cshell.doc: Checksum error. Is: $1, should be: 25555.
fi
#
#
echo Extracting ctlbrk.asm:
sed 's/^Z//' >ctlbrk.asm <<\STUNKYFLUFF
Z; Copyright (C) 1985 by Manx Software Systems, Inc.
Z; :ts=8
Z	include lmacros.h
Z
Zdataseg	segment	word public 'data'
Z	extrn	_PSP_:word
Z	_brkvec	dw ?
Z		dw ?
Z	localstk	dw	20	dup(?)
Z	stktop	label	word
Z	savess	dw ?
Z	savesp	dw ?
Zdataseg	ends
Z
Zourds	dw	0
Z
Z	assume	ds:dataseg
Z;
Z	procdef	ctl_brk_setup
Z	mov	ourds,ds
Z	push	ds
Z	mov	ax,3523H	;get cntl-break (cntl-c) handler
Z	int	21H
Z	mov	_brkvec,bx
Z	mov	_brkvec+2,es
Z	mov	dx,offset brk_handler
Z	mov	ax,cs
Z	mov	ds,ax
Z	mov	ax,2523H	;set new cntl-break handler
Z	int	21H
Z	pop	ds
Z	pret
Z	pend	ctl_brk_setup
Z
Z	procdef	ctl_brk_restore
Z	push	ds
Z	mov	dx,_brkvec
Z	mov	bx,word ptr _brkvec+2
Z	mov	ds,bx
Z	mov	ax,2523H	;restore old cntl-break handler
Z	int	21H
Z	pop	ds
Z	pret
Z	pend	ctl_brk_restore
Z
Zbrk_handler proc far
Z	;save ds and address our data segment
Z	push	ds
Z	mov	ds,ourds
Z	;move to the local stack after saving dos stack
Z	push	ax
Z	mov	savess,ss
Z	mov	savesp,sp
Z	mov	ax,ds
Z	mov	ss,ax
Z	mov	sp,offset stktop
Z	;save registers
Z	push	bp
Z	push	bx
Z	push	cx
Z	push	dx
Z	push	si
Z	push	di
Z	push	es
Z	mov	ah,051H	;find the current psp
Z	int	21H
Z	cmp	bx,_PSP_	;is it our program segment?
Z	je	noabort
Z	;set carry flag
Z	mov	ax,0FFFFH	;set up to shift bit into carry
Z	rcr	ax,1
Z	jmp	short done
Znoabort:
Z	;clear carry flag
Z	or	ax,ax		;should clear carry
Zdone:
Z	pop	es
Z	pop	di
Z	pop	si
Z	pop	dx
Z	pop	cx
Z	pop	bx
Z	pop	bp
Z	mov	ax,savess
Z	mov	ss,ax
Z	mov	sp,savesp
Z	pop	ax
Z	pop	ds
Z	jc	abortend
Z	iret
Zabortend:
Z	ret
Zbrk_handler endp
Z	finish
Z	end
STUNKYFLUFF
set `sum ctlbrk.asm`
if test 52105 != $1
then
echo ctlbrk.asm: Checksum error. Is: $1, should be: 52105.
fi
#
#
echo Extracting doprog.c:
sed 's/^Z//' >doprog.c <<\STUNKYFLUFF
Zdo_prog(argc,argv)
Zchar *argv[];
Z{
Z	int result;
Z	if (666 == (result = fexecvp(argv[0],argv)))
Z	{
Z		invalid(argc,argv);
Z		perror("");
Z		return -1;
Z	}
Z	return result; 
Z}
STUNKYFLUFF
set `sum doprog.c`
if test 44858 != $1
then
echo doprog.c: Checksum error. Is: $1, should be: 44858.
fi
#
#
echo Extracting drive.c:
sed 's/^Z//' >drive.c <<\STUNKYFLUFF
Z#define NULL (void *)0
Zdrive(argc,argv)
Z	char *argv[];
Z{
Z	char *dir,*getcwd();
Z
Z	bdos(0xe,**argv - 'a');	/* select drive 0 */
Z	if (NULL == (dir = getcwd(NULL,64)))
Z		return -1;
Z	free(dir);
Z	return 0;
Z}
STUNKYFLUFF
set `sum drive.c`
if test 37068 != $1
then
echo drive.c: Checksum error. Is: $1, should be: 37068.
fi
#
#
echo Extracting dump2.c:
sed 's/^Z//' >dump2.c <<\STUNKYFLUFF
Z
Z/*  dump.c  (10/83)  Debug style dump of a file from any starting position.
Z*/
Z#include <stdio.h>
Z#include <ctype.h>
Z#include <signal.h>
Z#include <setjmp.h>
Z#define FAIL        1
Z#define SUCCESS     0
Z#define TRUE      	(1)
Z#define FALSE       0
Z#define FOREVER     for (;;)
Z#define PAUSE       if (getch()=='\0') getch();
Z#define STDIN       0
Z#define STDOUT      1
Z#define STDERR      2
Zchar *dumpusage[] =
Z{
Z  "Usage: dump filespec [block [page]] | [segment:[offset]] [count]\r\n",
Z  "Where a block is 64K bytes and a page is 256 bytes.\r\n",
Z  "Segment:offset are standard 8086 notation in hexadecimal.\r\n",
Z  "Count is the number of bytes to dump in decimal.\r\n",
Z  NULL
Z};
Zchar *index();		/* suppress warning about conversion to int */
Z#define BUFSIZE 512
Ztypedef unsigned    ushort;
Z
Zstatic int _fmode = 0x8000;            /* Lattice 'c' - forces binary I/O */
Zextern short errno;             /* DOS 2.0 error number */
Zlong lseek();
Z
Zstatic char   buffer[BUFSIZE];         /* input buffer */
Zstatic ushort block = 0;               /* block number ( 64k bytes/block ) */
Zstatic ushort page = 0;                /* page number ( 256 bytes/page ) */
Zstatic ushort segment = 0;
Zstatic ushort offset = 0;
Zstatic long   filpos = 0;              /* beginning file position */
Zstatic long   count = 0x7FFFFFFFL;     /* number of bytes to dump */
Z
Zvoid ohw(),ohb(),onib(),abort();
Z
Zstatic jmp_buf env;
Zvoid (*signal())();
Zstatic void (*oldsig)();
Zstatic onintr()
Z{
Z	signal(SIGINT,SIG_IGN);	/* disable interrupts from kbd */
Z	longjmp(env,-1);
Z}
Z
Z#ifdef MAIN
Zmain
Z#else
Zdump
Z#endif
Z(argc,argv)                 /* DUMP ENTRY */
Zint  argc;
Zchar *argv[];
Z{
Z   char c;
Z   ushort i, numin, tot, file, cfrom;
Z   char *flag = 0;
Z   char *index();
Z   oldsig=signal(SIGINT,onintr);
Z   if (-1 == setjmp(env))
Z   {
Z   		close(file);
Z		write(2,"Interrupted\r\n",13);
Z		signal(SIGINT,oldsig);
Z		return -1;
Z   }
Z   if (argc < 2)
Z   {
Z   		char **u = (char **) dumpusage;
Z		while(*u)
Z		{
Z			write (2,*u,strlen(*u));
Z			++u;
Z		}
Z		return -1;
Z   }
Z   if ((file = open( argv[1], 0 )) == -1)
Z      abort( "cannot open", argv[1], errno );
Z
Z   if (argc > 2) {
Z      if ((flag = index( argv[2], ':' )) != NULL) {
Z         i = stch_i( argv[2], &segment );
Z         stch_i( argv[2]+i+1, &offset );
Z      }
Z      if (sscanf( argv[2], "%d", &block ) != 1)
Z         abort( "invalid block", argv[2], 0 );
Z   }
Z   if (argc > 3)
Z      if (sscanf( argv[3], "%d", &page ) != 1)
Z         abort( "invalid page", argv[3], 0 );
Z
Z   if ( flag ) {
Z      filpos = (long)segment*16L + (long)offset;
Z      tot = offset;
Z   }
Z   else {
Z      filpos = (block * 65536L) + (page * 256);
Z      tot = page * 256;
Z      segment = block * 4096;
Z   }
Z
Z   if (lseek( file, filpos, 0 ) == -1L)
Z      abort( "positioning to", argv[2], errno );
Z
Z   if (argc > 4)
Z      if (sscanf( argv[4], "%ld", &count ) != 1)
Z         abort( "invalid count", argv[4], 0 );
Z
Z
Z   do {                                    /* read & dump BUFSIZE bytes */
Z      numin = read( file, buffer, BUFSIZE );
Z      if (numin == -1)
Z         abort( "cannot read", argv[1], errno );
Z      cfrom=0;
Z      while (cfrom < numin) {
Z
Z         ohw(segment);                     /* print offset in hex */
Z         putchar(':');
Z         ohw(cfrom+tot);
Z         putchar(' ');
Z
Z         for (i=0; i < 16; i++) {          /* print 16 bytes in hex */
Z            putchar(' ');
Z            ohb(buffer[cfrom++]);
Z         }
Z
Z         putchar(' '); putchar(' '); putchar(' ');
Z
Z         cfrom -= 16;
Z         for (i=0; i < 16; i++) {          /* print 16 bytes in ASCII */
Z            c = buffer[cfrom] & 0x7f;
Z            if ( isprint(c) )              /* if printable character */
Z               putchar(c);
Z            else
Z               putchar('.');               /* else print period */
Z            cfrom++;
Z         }
Z
Z         putchar('\r'); putchar('\n');     /* print CR/LF */
Z
Z         if ((count -= 16) <= 0)             /* is count exhausted? */
Z            exit(0);
Z      }                                    /* end of while */
Z      tot += numin;
Z      if ( tot == 0 )
Z         segment += 4096;
Z   }                                       /* end of do */
Z   while (numin == BUFSIZE);
Z	signal(SIGINT,oldsig);					/* restore signal */
Z	return 0;
Z}                                          /* end of main */
Z
Zstatic void ohw(wrd)                        /*      print a word in hex     */
Zushort wrd;
Z{
Z   ohb( wrd>>8 );
Z   ohb( wrd );
Z}
Z
Zstatic void ohb(byt)                        /*      print a byte in hex     */
Zchar byt;
Z{
Z   onib( byt>>4 );
Z   onib( byt );
Z}
Z
Zstatic void onib(nib)            /*      print a nibble as a hex character   */
Zchar nib;
Z{
Z   nib &= 15;
Z   putchar((nib >= 10) ? nib-10+'A': nib+'0');
Z}
Z
Zstatic void abort( msg1 ,msg2 ,errno)     /*  print error msg1, msg2, and nbr */
Zchar *msg1,*msg2;                  /*   Does not close files.  */
Zshort errno;
Z{
Z   char stemp[10];
Z
Z   write( STDERR, "ERR: ", 5 );
Z   if (msg1)
Z      write( STDERR, msg1, strlen(msg1) );
Z   if (msg2)
Z      write( STDERR, " ", 1 );
Z      write( STDERR, msg2, strlen(msg2) );
Z   if (errno)   {
Z      sprintf( stemp," #%d", errno );
Z      write( STDERR, stemp, strlen(stemp) );
Z   }
Z   write( STDERR, "\r\n", 2 );
Z   longjmp(env,-1);
Z}
Z/** END DUMP **/
Z
Z#define BDOS_IN   7     /* input function for "getch" */
Z#define BDOS_OUT  6     /* output function for "putch" */
Z#define BDOS_CKS  11    /* check keyboard status for "kbhit" */
Z#define BDOS_BKI  10    /* buffered keyboardd input for "cgets" */
Z#define BDOS_PRT  9     /* print string for "cputs" */
Z
Zstatic char pushback;   /* character save for "ungetch" */
Z
Zstatic getch()
Z{
Zint c;
Z
Zif (pushback != '\0')
Z   {                    /* character was pushed back */
Z   c = pushback;
Z   pushback = '\0';
Z   return(c);
Z   }
Zreturn(bdos(BDOS_IN, 0xFF) & 127);
Z}
Zstatic putch(c)
Zchar c;
Z{
Zbdos(BDOS_OUT, c&127);
Zreturn(c);
Z}
Zstatic ungetch(c)
Zchar c;
Z{
Z
Zif (pushback != '\0') return(-1);
Zpushback = c;
Zreturn(c);
Z}
Zstatic char *cgets(s)
Zchar *s;
Z{
Zchar *p;
Z
Zif (*s == 0) *s = 250;          /* do not allow zero byte count */
Zbdos(BDOS_BKI, s);
Zp = s+2;
Zp[s[1]] = '\0';                 /* set terminating byte */
Zreturn(p);
Z}
Zstatic cputs(s)
Zchar *s;
Z{
Zchar *p;
Z
Zfor (p = s; *p != '\0'; p++) ;          /* find string terminator */
Z*p = '$';
Zbdos(BDOS_PRT, s);
Z*p = '\0';
Zreturn;
Z}
Z
Z
Zstatic int stch_i(p,r)
Z	char *p;
Z	int	*r;
Z{
Z	int count;
Z	int acc;
Z	int hdtoi();
Z	count = 0;
Z	*r = 0;
Z	while (-1 != (acc = hdtoi(*p++)))
Z	{
Z		++count;
Z		*r = (*r << 4) | acc;
Z	}
Z	return count;
Z}
Z
Zstatic hdtoi(c)
Z	char c;
Z{
Z	c = toupper(c);
Z	if (!isxdigit(c)) 
Z		return -1;
Z	if (isdigit(c))
Z		return (c - '0');
Z	return (c - 'A' + 10);
Z}
STUNKYFLUFF
set `sum dump2.c`
if test 53530 != $1
then
echo dump2.c: Checksum error. Is: $1, should be: 53530.
fi
#
#
echo Extracting echo.c:
sed 's/^Z//' >echo.c <<\STUNKYFLUFF
Z
Zecho(argc,argv)
Z	char *argv[];
Z{
Z	register int i;
Z	for (i = 1; i < argc;i++)
Z	{
Z		write(2,argv[i],strlen(argv[i]));
Z		if (i < argc-1)
Z			write(2," ",1);
Z	}
Z	crlf();
Z	return 0;
Z}
STUNKYFLUFF
set `sum echo.c`
if test 29675 != $1
then
echo echo.c: Checksum error. Is: $1, should be: 29675.
fi
#
#
echo Extracting env.c:
sed 's/^Z//' >env.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z
Z#define ENVSIZE 4000
Zextern int _PSP;
Zvoid *calloc();
Zchar *environment=NULL, *str_upper();
Zint env_paragraph;
Zchar *next_env;
Z#define envlimit(a) &a[ENVSIZE-1]
Z
Z#ifdef MAIN
Zmain
Z#else
Zset
Z#endif
Z(argc,argv)
Z	char *argv[];
Z{
Z	if (!environment)
Z		init_env();
Z	if (argc == 1)
Z	{
Z		show_env();
Z		return 0;
Z	}
Z	while(--argc)
Z	{
Z		add_env(str_upper(*(++argv)));
Z	}
Z	return 0;
Z}
Z
Zchar *
Zstr_upper(c)
Z	register char *c;
Z{
Z	register char *save = c;
Z	while(*c)
Z	{
Z		*c = toupper(*c);
Z		c++;
Z	}
Z	return save;
Z}
Z
Zchar *
Zstr_lower(c)
Z	register char *c;
Z{
Z	register char *save = c;
Z	while(*c)
Z	{
Z		*c = tolower(*c);
Z		c++;
Z	}
Z	return save;
Z}
Zinit_env()
Z{
Z	extern unsigned _dsval;	/* current data segment register value */
Z	long fudgefactor;
Z	register int c;
Z	unsigned envseg;
Z	unsigned offset = 0;
Z	envseg = peekw(0x2c,_PSP);
Z	environment = calloc(1,ENVSIZE+16);
Z	fudgefactor = (long)_dsval << 4;	/* convert to absolute paragraph */
Z	fudgefactor += (unsigned)environment + 16;
Z	fudgefactor &= 0xFFFF0L;
Z	env_paragraph = (int)((fudgefactor >>4) & 0xFFFF);
Z	environment = (char *) (fudgefactor - (long)(_dsval << 4));
Z	next_env = environment;
Z	while (c = peekb(offset,envseg))
Z	{
Z		while (c = peekb(offset++,envseg))
Z		{
Z			*next_env++ = c;
Z		}
Z		*next_env++ = '\0';
Z	}
Z}
Z
Zshow_env()
Z{
Z	register char *env;
Z	char c;
Z	for (env = environment;*env;)
Z	{
Z		while (c = *env++)
Z			write(1,&c,1);
Z		crlf();
Z	}
Z}
Z
Zstatic char *enverr = "No more environment space\r\n";
Zstatic char *enverr2 = "Improper environment string format!!\r\n";
Z
Zadd_env(string)
Z	char *string;
Z
Z{
Z	char *env_copy, *new, *index();
Z	char *old = environment;
Z	char *name_end,*new_name_end;
Z	int added = 0;
Z	int namelen;
Z
Z	if (NULL == (env_copy = new = calloc(1,ENVSIZE)))
Z	{
Z		write(2,enverr,strlen(enverr));
Z		return -1;
Z	}
Z
Z	while (*old)
Z	{
Z		if ( NULL == (name_end = index(old,'=')) || 
Z			NULL == (new_name_end = index(string,'=')) 
Z		)
Z		{
Z			write(2,enverr2,strlen(enverr2));
Z			free(env_copy);
Z			return -1;
Z		}
Z		namelen = (int)(name_end - old);
Z		if (!strncmp(old,string,namelen))
Z		{
Z			if (new_name_end[1])
Z			{
Z				/* if we don't have a string of the form name= */
Z				/* copy new string instead of old string */
Z				strcpy(new,string);
Z			}
Z			else
Z			/* if we have a set name= with no string then we want
Z			   to remove the string from the environment
Z			 */
Z				;
Z			added++;
Z		}
Z		else
Z		{
Z			strcpy(new,old);
Z		}
Z		new = &new[strlen(new)+1];
Z		old = &old[strlen(old)+1];
Z		if (new >= envlimit(new))
Z		{
Z			write(2,enverr,strlen(enverr));
Z			free(env_copy);
Z			return -1;
Z		}
Z	}
Z	if (!added)
Z	{
Z		strcpy(new,string);
Z	}
Z	new = &new[strlen(new)+1];
Z	/* copy the copy back to the environment */
Z	movmem(env_copy,environment,(int)(new-env_copy)+2);
Z	free(env_copy);
Z	return 0;
Z}
STUNKYFLUFF
set `sum env.c`
if test 56918 != $1
then
echo env.c: Checksum error. Is: $1, should be: 56918.
fi
#
#
echo Extracting fexec.asm:
sed 's/^Z//' >fexec.asm <<\STUNKYFLUFF
Z; Copyright (C) 1984 by Manx Software Systems
Z; :ts=8
Z	include lmacros.h
Zdataseg segment para public 'data'
Zparam	equ	this word
Zenv	dw	?
Zcline	dw	?,?
Zfcb1	dw	?,?
Zfcb2	dw	?,?
Z	extrn	errno_:word
Zdataseg	ends
Z	assume	ds:dataseg
Zsave_ss	dw	0
Zsave_sp	dw	0
Z	procdef	fexec,<<filname,ptr>,<enva,word>,<clinea,ptr>,<fcb1a,ptr>,<fcb2a,ptr>>
Z;			char *fexec(name,env,cline,fcb1,fcb2)
Z;
Z	push	si
Z	push	di
Z	pushf
Z	push	[030H]
Z	push	[02EH]
Z	push	ds
Z	push	es
Z	mov	cs:save_ss,ss
Z	mov	cs:save_sp,sp
Z;
Z;	set up parameter block for exec call
Z;
Z	mov	ax,enva
Z	mov	env,ax
Zifndef LONGPTR
Z	mov	ax,ds
Z	mov	es,ax
Zendif
Z	ldptr	ax,clinea,es
Z	mov	cline,ax
Z	mov	cline+2,es
Z	ldptr	ax,fcb1a,es
Z	mov	fcb1,ax
Z	mov	fcb1+2,es
Z	ldptr	ax,fcb2a,es
Z	mov	fcb2,ax
Z	mov	fcb2+2,es
Z;
Z	mov	ax,ds
Z	mov	es,ax
Z	mov	bx,offset param
Z	ldptr	dx,filname,ds		;name of file to exec
Z	mov	ax,04b00H
Z	int	21h
Z	mov	ss,cs:save_ss
Z	mov	sp,cs:save_sp
Z	pop	es
Z	pop	ds
Z	jnc	noerror
Z	mov	errno_,ax
Z	mov	ax,666
Z	jmp	short done
Znoerror:
Z	sub	ax,ax
Zdone:
Z	pop	[02EH]
Z	pop	[030H]
Z	popf
Z	pop	di
Z	pop	si
Z	pret
Z	pend	fexec
Z	finish
Z	end
STUNKYFLUFF
set `sum fexec.asm`
if test 35466 != $1
then
echo fexec.asm: Checksum error. Is: $1, should be: 35466.
fi
#
#
echo Extracting fexecv.c:
sed 's/^Z//' >fexecv.c <<\STUNKYFLUFF
Z#ifdef DEBUG
Z#ifndef FILE
Z#include <stdio.h>
Z#endif
Z#include <ctype.h>
Z#include <debug.h>
Z#endif
Z
Z
Z/* Copyright (C) 1983, 1984 by Manx Software Systems */
Z/* modified by kent williams to employ environment managed in env.c */
Zextern int env_paragraph;
Z
Zfexecv(path, argv)
Zchar *path, **argv;
Z{
Z	register char *cp, *xp;
Z	int i;
Z	char buffer[258];
Z	char fcb1[16], fcb2[16];
Z
Z	cp = buffer+1;
Z	i = 1;
Z	if (*argv) {
Z		++argv;			/* skip arg0, used for unix (tm) compatibility */
Z		while (xp = *argv++) {
Z			if (i == 1)
Z				fcbinit(xp, fcb1);
Z			else if (i == 2)
Z				fcbinit(xp, fcb2);
Z			while (*xp) {
Z				if (cp >= buffer+256)
Z					goto done;
Z				*cp++ = *xp++;
Z			}
Z			*cp++ = ' ';
Z			++i;
Z		}
Z	}
Zdone:
Z	buffer[0] = cp - (buffer+2);
Z	/* terminate string */
Z	buffer[buffer[0]+1] = 0;
Z#ifdef DEBUG
Z		fprintf(stderr,"\nbuffer[0] = %d\n",buffer[0]);
Z		for (i = 1; buffer[i] ; i++)
Z		{
Z			if (isprint(buffer[i]))
Z				putchar(buffer[i]);
Z			else
Z				fprintf("buffer[%d] = %d\n",i,buffer[i]);
Z		}
Z		crlf();
Z#endif
Z	return fexec(path, env_paragraph, buffer, fcb1, fcb2);
Z}
Z
STUNKYFLUFF
set `sum fexecv.c`
if test 22589 != $1
then
echo fexecv.c: Checksum error. Is: $1, should be: 22589.
fi
#
#
echo Extracting fexecvp.c:
sed 's/^Z//' >fexecvp.c <<\STUNKYFLUFF
Zfexecvp(name, argv)
Zchar *name, **argv;
Z{
Z	register char *cp, *xp;
Z	int result;
Z	char *getenv(), path[64];
Z
Z	if (666 != (result = tryexec("", name, argv)))
Z		return result;
Z	if ((cp = getenv("PATH")) != 0) {
Z		while (*cp) {
Z			xp = path;
Z			while (*cp) {
Z				if (*cp == ';') {
Z					++cp;
Z					break;
Z				}
Z				*xp++ = *cp++;
Z			}
Z			*xp = 0;
Z			if (path[0] != 0)
Z				if (666 != (result = tryexec(path, name, argv)))
Z					return result;
Z		}
Z	}
Z	return 666;
Z}
Z
Zstatic
Ztryexec(dir, name, argv)
Zchar *dir, *name, **argv;
Z{
Z	char newname[64];
Z	register char *cp;
Z	char *rindex(),*index();
Z
Z	strcpy(newname, dir);
Z	if (((cp = index(newname, '/')) || (cp = index(newname, '\\')))
Z				&& *(cp+1) != '\0')
Z		strcat(newname, "/");
Z	strcat(newname, name);
Z	if (index(name, '.') == 0) {
Z		strcat(newname, ".com");
Z		if (666 != fexecv(newname, argv))
Z			return wait();
Z		strcpy(rindex(newname,'.'), ".exe");
Z	}
Z	if (666 != fexecv(newname, argv))
Z			return wait();
Z	return 666;
Z}
STUNKYFLUFF
set `sum fexecvp.c`
if test 44134 != $1
then
echo fexecvp.c: Checksum error. Is: $1, should be: 44134.
fi
#
#
echo Extracting fgrep.c:
sed 's/^Z//' >fgrep.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <fcntl.h>
Z
Z#include <signal.h>
Z#include <setjmp.h>
Z
Zvoid (*signal())();
Zvoid (*fgrepsig)();
Zjmp_buf fgrep_env;
Z
Zvoid fgrep_intr()
Z{
Z	/* restore old signal */
Z	signal(SIGINT,fgrepsig);
Z	/* jump to exit */
Z	longjmp(fgrep_env,-1);
Z}
Z
ZFILE *fopen(),*fdopen();
Z
Zchar *fgets();
Z
Zfgrep( argc, argv )
Zint	argc;
Zchar * argv[];
Z	{
Z	FILE *  fd;
Z	int	rc;
Z	char * pattern;
Z	/* handle interrupts */
Z	if (-1 == setjmp(fgrep_env))
Z	{
Z		static char *intmsg = "Interrupted\r\n";
Z		write(2,intmsg,strlen(intmsg));
Z		fclose(fd);
Z		return -1;
Z	}
Z	/* set signal catcher */
Z	fgrepsig= signal(SIGINT,fgrep_intr);
Z	while( --argc )
Z		{
Z		if( (*++argv)[0] == '-' )
Z			switch( (*argv)[1] )
Z				{
Z				default:
Z					fprintf( stderr, "invalid argument %s\n", *argv );
Z					return(1);
Z				}
Z		else
Z			break;
Z		}
Z
Z	if( argc == 0 )
Z		{
Z		fprintf( stderr, "usage: fgrep pattern [file ...]\n" );
Z		return(1);
Z		}
Z
Z	pattern = *argv++;
Z	argc--;
Z
Z	rc = 0;
Z	if( argc == 0 )
Z		{
Z		fd = fdopen(0,"r");
Z		rc = _fgrep( NULL, pattern, fd);
Z		fclose(fd);
Z		return( 0 );
Z		}
Z	else
Z	while( argc-- )
Z		{
Z
Z		if( (fd = fopen( *argv, "r" )) == NULL )
Z			fprintf( stderr, "couldn't open %s\n", *argv );
Z		else
Z			{
Z			rc |= _fgrep( *argv, pattern, fd );
Z			fclose( fd );
Z			}
Z		argv++;
Z
Z		}
Z
Z	return( !rc );
Z	signal(SIGINT,fgrepsig);
Z	}
Z
Z
Z_fgrep( file, pattern, fd )
Zchar	* file;
Zchar	* pattern;
ZFILE *	fd;
Z	{
Z	char	line[BUFSIZ];
Z	int		rc;
Z	int		linenumber = 1;
Z	rc = 0;
Z	while( fgets( line, sizeof(line), fd ) != NULL )
Z	{
Z		if( rc = match( pattern, line ) )
Z			printf( "%s %d: %s", (file) ? file : "stdin", linenumber, line );
Z		linenumber++;
Z	}
Z	return( rc );
Z
Z	}
Z
Zmatch( pattern, line )
Zregister char	* pattern;
Zchar	* line;
Z	{
Z	/*  not a great algorithm  */
Z	register char * ptr;
Z	char	* end;
Z	int	plen = strlen(pattern);
Z	int llen = strlen(line);
Z
Z	if( plen > llen )
Z		return( 0 );
Z
Z	end = line+(llen-plen);
Z
Z	for( ptr=line; ptr < end; ptr++ )
Z		{
Z		if( strncmp( pattern, ptr, plen ) == 0 )
Z			return( 1 );
Z		}
Z
Z	return( 0 );
Z	}
STUNKYFLUFF
set `sum fgrep.c`
if test 15656 != $1
then
echo fgrep.c: Checksum error. Is: $1, should be: 15656.
fi
echo ALL DONE BUNKY!
exit 0
-- 
                                                                          --
                .^.                        michael regoli 
                /|\        ...ihnp4!inuxc!iuvax!isrnix!mr 
               '|!|`                     <mr@isrnix.UUCP> 

mr@isrnix.UUCP (michael regoli) (12/15/85)

#########################################################
#                                                       #
# This is a shell archive file.  To extract files:      #
#                                                       #
#    1)	Make a directory for the files.                 #
#    2) Write a file, such as "file.shar", containing   #
#       this archive file into the directory.           #
#    3) Type "sh file.shar".  Do not use csh.           #
#                                                       #
#########################################################
#
#
echo Extracting getcmd.c:
sed 's/^Z//' >getcmd.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <fcntl.h>
Z
Zchar *gets();
Zchar rest[256];
Zchar *rptr = NULL;
Zchar pipeactive = 0;
Zcurrname = 0;
Zchar *pipename[2] = {
Z	"shtmp1",
Z	"shtmp2"};
Zchar *
Zgetnextcmd(buf)
Z	char *buf;
Z{
Z	char *fgets(),*index(),*pipe,*semi;
Z	if (!rptr)
Z	{
Z		register char *c;
Z		if (0 == read(0,rest,sizeof(rest)))
Z			return NULL;
Z		c = rptr = rest;
Z		while (*c)
Z		{
Z			if (*c == '\r' || *c == '\n')
Z			{
Z				*c = '\0';
Z				break;
Z			}
Z			++c;
Z		}
Z
Z	}
Z	pipe = index(rptr,'|');
Z	semi = index(rptr,';');
Z	if (pipe == NULL && semi == NULL)
Z	{
Z		strcpy(buf,rptr);
Z		if (pipeactive)
Z		{
Z			pipeactive = 0;
Z			strcat(buf," < ");
Z			strcat(buf,pipename[currname]);
Z		}
Z		rptr=NULL;
Z	}
Z	/* one or the other, or both are not NULL, so comparison is in order */
Z	else if (pipe && (!semi || (pipe < semi)))
Z	{
Z		*pipe = '\0';	/* terminate string */
Z		strcpy(buf,rptr); /* copy to buf */
Z		rptr = pipe+1;	/* set up rest */
Z		if (pipeactive++)
Z		{
Z			pipeactive = 1;
Z			strcat(buf," < ");
Z			strcat(buf,pipename[currname]);
Z		}
Z		strcat(buf," > ");
Z		currname ^= 1;	/* flip flop pipe names */
Z		strcat(buf,pipename[currname]);
Z	}
Z	else if (semi && (!pipe || (semi < pipe)))
Z	/* we have a semicolon to deal with */
Z	{
Z		*semi = '\0';
Z		strcpy(buf,rptr);
Z		rptr = semi+1;
Z		if (pipeactive)
Z		{
Z			pipeactive = 0;
Z			strcat(buf," < ");
Z			strcat(buf,pipename[currname]);
Z		}
Z	}
Z	return buf;
Z}
STUNKYFLUFF
set `sum getcmd.c`
if test 06449 != $1
then
echo getcmd.c: Checksum error. Is: $1, should be: 06449.
fi
#
#
echo Extracting invalid.c:
sed 's/^Z//' >invalid.c <<\STUNKYFLUFF
Z
Zinvalid(argc,argv)
Z	char *argv[];
Z{
Z	register int i;
Z	static char *invmsg = "sh : bad command : ";
Z	write(2,invmsg,strlen(invmsg));
Z	for (i = 0; i < argc;i++)
Z	{
Z		write(2,argv[i],strlen(argv[i]));
Z		write(2," ",1);
Z	}
Z	return -1;
Z}
STUNKYFLUFF
set `sum invalid.c`
if test 00995 != $1
then
echo invalid.c: Checksum error. Is: $1, should be: 00995.
fi
#
#
echo Extracting ls.c:
sed 's/^Z//' >ls.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z
Ztypedef struct
Z{
Z	char attribute;
Z	unsigned file_time;
Z	unsigned file_date;
Z	long file_size;
Z	char file_name[13];
Z} file_desc;
Z
Ztypedef struct
Z{
Z	char dos_reserved[21];
Z	file_desc file;
Z} fcb;
Z
Z#define maxfiles 128 
Z
Zchar printbuf[256];
Zfile_desc *getfirst(),*getnext();
Zchar *index(), *rindex();
Zint mode = 0x10;
Zint verbose=0,column=4,recurse=0;
Zint quiet = 0;
Zint drivenum = 0;
Zlong time();
Zshort year;
Z
Zdo_return(result)
Z{
Z	verbose = quiet = recurse = 0;
Z	column = 4;
Z	return result;
Z}
Z#ifndef MAIN
Zls
Z#else
Zmain
Z#endif
Z(argc,argv)
Zchar *argv[];
Z{
Z	int noargs;
Z	char *current;
Z	char namebuf[128];
Z	/* 
Z	 * initialize statics
Z	 */
Z	mode = 0x10;
Z	verbose=0;column=4;recurse=0;
Z	quiet = 0;
Z	drivenum = 0;
Z	/*
Z	 * get current time
Z	 */
Z	year = (int)((time(NULL) >> 25) & 0x7F) + 80;
Z	/*
Z	 * set up a default search name
Z	 */
Z	if (noargs = (argc == 1))
Z		argc++;
Z	while(--argc)
Z	{
Z		if (noargs)
Z			current = "*.*";
Z		else
Z			current = *(++argv);	/* get current file name */
Z		if (*current == '-')
Z		{
Z			++current;	/* point past - */
Z			while (*current)
Z			{
Z				switch (*current++)
Z				{
Z				case 'l':
Z				case 'L':
Z					verbose = 1;
Z					if (column != 1)
Z						column = 2;
Z					if (quiet)
Z					{
Z						fprintf(stderr,"ls : verbose and quiet conflict\n");
Z						do_return(-1);
Z					}
Z					break;
Z				case 'q':
Z				case 'Q':
Z					quiet = 1;
Z					if (verbose)
Z					{
Z						fprintf(stderr,"ls : quiet and verbose conflict\n");
Z						do_return(-1);
Z					}
Z					break;
Z				case 'c':
Z				case 'C':
Z					column = 1;
Z					break;
Z				case 'a':
Z				case 'A':
Z					mode = 0x2 + 0x4 + 0x10;
Z					break;
Z				case 'r':
Z				case 'R':
Z					recurse = 1;
Z					mode = 0x2 + 0x4 + 0x10;
Z					break;
Z				default:
Z					break;
Z				}
Z			}
Z			/* if we're down to one argument after looking at all the
Z			   switches, we need to set noargs to true */
Z			if (noargs = (argc == 1))
Z				argc++;
Z			continue;
Z		}
Z		/* if a drive is specified, figure out what drive it is */
Z		if (current[1] == ':')
Z		{
Z			drivenum = toupper(current[0]) - 'A' + 1;
Z		}
Z		else
Z			drivenum = 0;
Z		/* if no wild cards, look for directory and drive names */
Z		if ( NULL == index(current,'?') && NULL == index(current,'*'))
Z		{
Z			if (getfirst(current)->attribute & 0x10)
Z			{
Z				strcpy(namebuf,current);
Z				strcat(namebuf,"\\*.*");
Z				current = namebuf;
Z			} 
Z			/* look for drive names */
Z			else if (current[strlen(current)-1] == ':' && 
Z						!current[strlen(current)])
Z			{
Z				strcpy(namebuf,current);
Z				strcat(namebuf,"\\*.*");
Z				current = namebuf;
Z			}
Z		}
Z		do_dir(current);
Z	}
Z	do_return( 0);
Z}
Z
Zdo_dir(current)
Z	char *current;
Z{
Z	typedef file_desc fblock[maxfiles];	/* as many as we'll likely need */
Z	file_desc *files;
Z	file_desc *curr_file,*getnext();
Z	void *malloc();
Z	unsigned int ftime,date;
Z	int i,j,col;
Z	int files_cmp();
Z	long total = 0;
Z	char atts[4]; /* drw */
Z	/* allocate file block */
Z	if (NULL == (files = malloc(sizeof(fblock))))
Z	{
Z		fprintf(stderr,"Not enough memory to do directory\n");
Z		return -1;
Z	}
Z	/* look for match */
Z	i = 0;
Z	if (!(curr_file = getfirst(current)))
Z	{
Z		printf(stderr,"ls : no files matching %s\n",current);
Z		free(files);
Z		return;	
Z	}
Z	files[i++] = *curr_file;
Z	/* get all matching */
Z	while ((curr_file = getnext()) && i < maxfiles)
Z		files[i++] = *curr_file;	
Z	if (i > 1)
Z		qsort(files,i,sizeof(file_desc),files_cmp);
Z	if (!quiet)
Z	{
Z		write(1,"\r\n",2);
Z		write(1,current,strlen(current));
Z		write(1,"\r\n",2);
Z	}
Z	col = 1;
Z	for (j = 0; j < i; j++)
Z	{
Z		register char *c = files[j].file_name;
Z		if (*c == '.')
Z			continue;	/* filter out . and .. */
Z		while (*c)
Z		{
Z			*c = tolower(*c);
Z			c++;
Z		}
Z		if (verbose)
Z		{
Z			register char att = files[j].attribute;
Z			register int fyear;
Z			fyear = ((files[j].file_date >> 9) & 0x7F)+80; 
Z			atts[3] = 0;	/* terminate string */
Z			atts[0] = att & 0x10 ? 'd' : '-';
Z			atts[1] = att & 2 ? '-' : 'r';
Z			atts[2] = att & 1 ? '-' : 'w';
Z			if (atts[0] == 'd')
Z			{
Z				register int k;
Z				sprintf(printbuf,"%s %s\\",atts,files[j].file_name);
Z				write(1,printbuf,strlen(printbuf));
Z				k = 12 - strlen(files[j].file_name)+7;
Z				while(k--)
Z					write(1," ",1);
Z
Z			}
Z			else
Z			{
Z				total += files[j].file_size;
Z				sprintf(printbuf,"%s %-12s %-6ld ",atts,files[j].file_name,
Z						files[j].file_size);
Z				write(1,printbuf,strlen(printbuf));
Z			}
Z			ftime = files[j].file_time;
Z			date = files[j].file_date;
Z			if (year == fyear)
Z			{
Z				sprintf(printbuf,"%02d/%02d %02d:%02d ",
Z					((date >> 5) & 0x0F),	/* month */
Z					date & 0x1F,		/* day	*/
Z					(ftime >> 11) & 0x1F,		/* hours */
Z					(ftime >> 5) & 0x3F);		/* minutes */
Z				write(1,printbuf,strlen(printbuf));
Z			}
Z			else
Z			{
Z				sprintf(printbuf,"%02d/%02d       ",
Z					((date >> 5) & 0x0F),	/* month */
Z					fyear,					/* file year */
Z					(ftime >> 11) & 0x1F,		/* hours */
Z					(ftime >> 5) & 0x3F);		/* minutes */
Z				write(1,printbuf,strlen(printbuf));
Z			}
Z		}
Z		else
Z		{
Z			if (files[j].attribute & 0x10)
Z			{
Z				register int k;
Z				sprintf(printbuf,"%s\\",files[j].file_name);
Z				write(1,printbuf,strlen(printbuf));
Z				k = 16 - strlen(files[j].file_name);
Z				while(--k)
Z					write(1," ",1);
Z
Z			}
Z			else
Z			{
Z				sprintf(printbuf,"%-13s   ",files[j].file_name);
Z				write(1,printbuf,strlen(printbuf));
Z			}
Z		}
Z		if (col == column)
Z		{
Z			col = 1;
Z			write(1,"\r\n",2);
Z		}
Z		else
Z			col++;
Z	}
Z	write(1,"\r\n",2);
Z	if (verbose)
Z	{
Z		sprintf(printbuf,"%ld bytes in %d files ",total,i);
Z		write(1,printbuf,strlen(printbuf));
Z		pr_freespace();
Z	}
Z	if (recurse)
Z		for (j = 0; j < i; j++)
Z		{
Z			/* we've got a subdirectory */
Z			if (files[j].attribute & 0x10 && files[j].file_name[0] != '.')
Z			{
Z				char *path;
Z				char dirname[48];
Z				if (!strcmp(current,"*.*"))
Z					dirname[0] = '\0';
Z				else
Z					strcpy(dirname,current);
Z				if (path = rindex(dirname,'\\'))
Z					*(++path) = '\0';
Z				strcat(dirname,files[j].file_name);	/* get name */
Z				strcat(dirname,"\\*.*");
Z				do_dir(dirname);
Z			}
Z		}
Z	free(files);
Z}
Z
Zfiles_cmp(a,b)
Z	file_desc *a,*b;
Z{
Z	return strcmp(a->file_name,b->file_name);
Z}
Z
Zfcb tmp;
Z
Zfile_desc *getfirst(fname)
Z	char *fname;
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&tmp);
Z	result = bdos(0x4E,fname,mode);
Z	/* make the find first call */
Z	if(2 == result || 18 == result)
Z		return NULL;
Z	return &(tmp.file);
Z}
Z
Zfile_desc *getnext()
Z{
Z	register int result;
Z	/* set the disk transfer address */
Z	bdos(0x1A,&tmp);
Z	result = bdos(0x4f,0,0);
Z	/* make the find next call */
Z	if (18 == result)
Z		return NULL;
Z	return &(tmp.file);
Z}
Z
Z/*	determine available space on default drive
Z*/
Z
Zpr_freespace()
Z{
Z	/* register arguments for INT */
Z    struct   {int ax,bx,cx,dx,si,di,ds,es;}sysr;
Z    unsigned int    retstat;		    /* flags returned from INT */
Z
Z#define 	    cls_avail  sysr.bx	    /* number of available clusters */
Z#define 	    cls_total  sysr.dx	    /* total number of clusters on volume */
Z#define 	    byt_sectr  sysr.cx	    /* bytes per sector */
Z#define 	    sec_clstr  sysr.ax	    /* sectors per cluster */
Z
Z    unsigned long   byt_clstr,		    /* bytes per cluster */
Z		    vol_total,		    /* total size of volume, bytes */
Z		    fre_total;		    /* size of free space, bytes */
Z
Z    sysr.ax = 0x3600;		    /* get disk free space function code */
Z    sysr.dx = drivenum;			    /* (DL) = drive id, 0=current, 1=A, */
Z    retstat = sysint(0x21,&sysr,&sysr);		    /* invoke DOS */
Z    if( sec_clstr!=0xffff ) 
Z	{
Z		byt_clstr = byt_sectr * sec_clstr;
Z		vol_total = cls_total * byt_clstr;
Z		fre_total = cls_avail * byt_clstr;
Z		fprintf(stdout,
Z		"%lu out of %lu bytes available on %c:\n", 
Z		fre_total,vol_total, 
Z		drivenum ? drivenum + 'A' - 1 : bdos(0x19,0,0)+'A');
Z	}
Z}
STUNKYFLUFF
set `sum ls.c`
if test 06358 != $1
then
echo ls.c: Checksum error. Is: $1, should be: 06358.
fi
#
#
echo Extracting main.c:
sed 's/^Z//' >main.c <<\STUNKYFLUFF
Z#line 1 "main.s"
Z#include <stdio.h>
Z#include <ctype.h>
Z#include <signal.h>
Z#include <fcntl.h>
Z#include <setjmp.h>
Ztypedef struct		/* used to find builtin commands */
Z{
Z	char *cmdname;
Z	int (*func)();
Z} builtin;
Z
Zextern builtin commands[];
Zextern char histerr[];
Zextern int j,hiscount;
Zextern char *history[];
Zextern int histsize;
Zextern int numcmds;
Zstatic int quiet = 0;
Z
Zchar *version = "SHELL VERSION 1.2 Kent Williams";
Z
Zjmp_buf env;
Z
Zchar *pipename[] =
Z{
Z	"\\shtmp1",
Z	"\\shtmp2"
Z};
Z
Zchar cmdbuf[512];
Zint  currname = 0;
Zint result = 0;
Z
Zmain(argc,argv)
Z	char *argv[];
Z{
Z	signal(SIGINT,SIG_IGN);	/* ignore breaks */
Z
Z	quiet = !isatty(0);		/* quiet = batch shell */
Z
Z	/* initialize local environment */
Z	init_env();
Z	cli();
Z	exit(0);
Z}
Z
Z#ifndef SNODEBUG
Z#define SNODEBUG
Z#endif
Z#line 46 "main.s"
Z
Z/*
Z * statemachine cli
Z */
Zcli () 
Z
Z#line 48 "main.s"
Z{
Z#line 49 "main.s"
Z/* global variables */
Z	int i;
Z	int repeat, state, inpipe = 0;
Z	static char localbuf[256];
Z	static char histbuf[256];
Z	static char tail[256];
Z	int histindex,argindex,takeline;
Z	char *local = localbuf;
Z	char *current,*curr_save;
Z	char *ntharg(), *argptr;
Z	char *savestr();
Z
Z
Z/*
Z * end of declarations for cli
Z */
Z/* $ */ goto getline;
Z#line 62 "main.s"
Z
Zgetline:
Z{
Z#line 62 "main.s"
Z		/* kill tmp files */
Z		unlink(pipename[0]); unlink(pipename[1]);
Z
Z		hiscount = j % histsize; /* hiscount is current position in history */
Z		if(!quiet)
Z			fprintf(stderr,"%d%% ",j);
Z
Z/*
Z * The following code simply reads a line from standard input.
Z * It is so complicated because when you save the standard stream
Z * files and execute another program/command, standard input is
Z * left in an uncertain state - the FILE stdin seems to be at EOF,
Z * even when standard input is associated with the console, and
Z * cr/lf combinations show up as line terminators, whereas usually
Z * only linefeeds get placed in the input stream.
Z * WHY? beats me.  Something could be wrong with
Z *  1. AZTEC C runtime
Z *  2. PCDOS
Z *  3. Me
Z *  4. All three, or permutations of 1-3 reducto ad absurdum.
Z * All I know is this works
Z */
Z		/* clear command buffer so string read is null terminated */
Z		setmem(cmdbuf,sizeof(cmdbuf),0);
Z		for (current = cmdbuf;;current++)
Z		{
Z			int readresult;
Z			if ((readresult = read(0,current,1)) == 0 ||
Z				readresult == -1)
Z			{
Z/* $ */ goto terminal;
Z			}
Z			if (*current == '\r')
Z			{
Z				if ((readresult = read(0,current,1)) == 0 ||
Z					readresult == -1)
Z				{
Z/* $ */ goto terminal;
Z				}
Z				*current = '\0';
Z				break;
Z			}
Z			else if (*current == '\n')
Z			{
Z				*current = '\0';	/* terminate string */
Z				break;
Z			}
Z		}
Z		current = cmdbuf;	/* point current at start of buffer */
Z/*
Z * end of input weirdness
Z */
Z		/* if we're recycling history strings, free previous one */
Z		if (history[hiscount])
Z			free(history[hiscount]);
Z
Z		/* save current in history array */
Z		history[hiscount] = savestr(current);
Z		/* parse command for compound statements and pipes */
Z		local = localbuf;	/* set pointer to state of buffer */
Z		setmem(localbuf,sizeof(localbuf),0);	/* clear buffer */
Z/* $ */ goto eatwhitespace;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate getline
Z */
Z};
Z#line 125 "main.s"
Z
Z
Zcharstate:
Z{
Z#line 127 "main.s"
Z	switch(*current)
Z	{
Z	case '\0':
Z		*local = '\0';
Z		current++;
Z/* $ */ goto emit;
Z	case '"' :
Z		*local++ = *current++;
Z/* $ */ goto doublequotes;
Z	case '/' :
Z		*local++ = '\\';
Z		current++;
Z/* $ */ goto charstate;;
Z	case '\'':
Z		*local++ = *current++;
Z/* $ */ goto singlequotes;
Z	case '\\':
Z		*local++ = *++current;
Z		current++;
Z/* $ */ goto charstate;
Z	case ';':
Z		*local = '\0';
Z		current++;
Z/* $ */ goto compound;
Z	case '|':
Z		*local = '\0';
Z		current++;
Z/* $ */ goto pipe;
Z	case '!':
Z		current++;
Z/* $ */ goto histstate;
Z	default:
Z		*local++ = *current++;
Z/* $ */ goto charstate;
Z	}
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate charstate
Z */
Z};
Z#line 163 "main.s"
Z
Z
Zemit:
Z{
Z#line 165 "main.s"
Z	if (inpipe)
Z	{
Z		inpipe = 0;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	command(localbuf);
Z/* $ */ goto done;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate emit
Z */
Z};
Z#line 174 "main.s"
Z
Z
Zcompound:
Z{
Z#line 176 "main.s"
Z	if (inpipe)
Z	{
Z		inpipe = 0;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	command(localbuf);
Z	local = localbuf;
Z	setmem(localbuf,sizeof(localbuf),0);	/* clear buffer */
Z/* $ */ goto eatwhitespace;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate compound
Z */
Z};
Z#line 187 "main.s"
Z
Z
Zsinglequotes:
Z{
Z#line 189 "main.s"
Z	switch (*current)
Z	{
Z	case '\0':
Z		write(2,"No closing quotes!!\r\n",21);
Z/* $ */ goto parserr;
Z	case '\'':
Z		*local++ = *current++;
Z/* $ */ goto charstate;
Z	default:
Z		*local++ = *current++;
Z/* $ */ goto singlequotes;
Z	}
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate singlequotes
Z */
Z};
Z#line 202 "main.s"
Z
Z
Zdoublequotes:
Z{
Z#line 204 "main.s"
Z	switch(*current)
Z	{
Z	case '\0':
Z		write(2,"No closing quotes!!\r\n",21);
Z/* $ */ goto done;
Z	case '"':
Z		*local++ = *current++;
Z/* $ */ goto charstate;
Z	default:
Z		*local++ = *current++;
Z/* $ */ goto doublequotes;
Z	}
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate doublequotes
Z */
Z};
Z#line 217 "main.s"
Z
Z
Zhiststate:
Z{
Z#line 219 "main.s"
Z	/* handle history substitutions */
Z	setmem(histbuf,sizeof(histbuf),0);	/* clear buffer */
Z
Z	/* save current pointer into command buffer */
Z	curr_save = current;
Z
Z	/* copy command head */
Z	strncpy(histbuf,cmdbuf,(int)(current-cmdbuf)-1);
Z
Z	/* takeline means take all arguments past current one */
Z	takeline = 0;
Z
Z	/* parse history expression */
Z	switch (*current)
Z	{
Z	case '!':	/* last command line */
Z		if (j)	/* special case first time through */
Z		{
Z			histindex = hiscount ? hiscount - 1 : histsize - 1;
Z		}
Z		else
Z		{
Z			/* force error condition */
Z			write(2,histerr,strlen(histerr));
Z/* $ */ goto parserr;
Z		}
Z		current++;	/* point to next */
Z		break;
Z	case '-':		/* negative (relative #) */
Z	/* a particular numbered command */
Z	case '0':
Z	case '1':
Z	case '2':
Z	case '3':
Z	case '4':
Z	case '5':
Z	case '6':
Z	case '7':
Z	case '8':
Z	case '9':
Z		/* repeat numbered command */
Z		repeat = atoi(current);
Z		if (repeat < 0)	/* handle relative addressing */
Z			repeat += j;
Z
Z		/* if command is within range */
Z		if ((j - repeat) <= histsize && repeat < j)
Z		{
Z			histindex = repeat % histsize;
Z		}
Z		else
Z		{
Z/* $ */ goto parserr;
Z		}
Z
Z		/* skip past numeric expression */
Z		while(isdigit(*current)||*current=='-')
Z			++current;
Z		break;
Z	default:
Z		write(2,"Bad history expression\r\n",24);
Z/* $ */ goto parserr;
Z	}
Z	/* look for particular argument substitutions */
Z	switch (*current)
Z	{
Z	/* we want the whole enchilada */
Z	case '\0':
Z	case '\t':
Z	case '\r':
Z	case '\n':
Z	case ' ':
Z		strcat(histbuf,history[histindex]);
Z		break;
Z	case ':':
Z		++current;	/* point past colon */
Z		switch (*current)
Z		{
Z		case '^':
Z			argindex = 1;
Z			++current;
Z			break;
Z		case '0':
Z		case '1':
Z		case '2':
Z		case '3':
Z		case '4':
Z		case '5':
Z		case '6':
Z		case '7':
Z		case '8':
Z		case '9':
Z			/* index of argument */ 
Z			argindex = atoi(current);
Z			while(isdigit(*current))
Z				++current;
Z			if (*current == '*')
Z			{
Z				takeline = 1;
Z				current++;
Z			}
Z			break;
Z		case '$':
Z			argindex = lastarg(history[histindex]);
Z			current++;
Z			break;
Z		case '*':
Z			takeline = 1;	/* take arg 1 through arg n */
Z			argindex = 1;
Z			current++;
Z			break;
Z		default:
Z/* $ */ goto parserr;
Z		}
Z		/* pick up pointer to argument in history we need */
Z		if (takeline == 0)
Z		{
Z			if (NULL == 
Z				(argptr = ntharg(history[histindex],argindex)))
Z			{
Z/* $ */ goto parserr;
Z			}
Z			strcat(histbuf,argptr);
Z		}
Z		else
Z		{
Z			while (NULL !=
Z				(argptr = ntharg(history[histindex],argindex++)))
Z			{
Z				strcat(histbuf,argptr);
Z				strcat(histbuf," ");
Z			}
Z		}
Z	}
Z	/* history substitutions */
Z	/* copy command buffer tail to tail buffer */
Z	strcpy(tail,current);
Z	/* copy histbuf back to cmdbuf */
Z	strcpy(cmdbuf,histbuf);
Z	/* point current at history substitution to continue parsing */
Z	current = --curr_save; /* -1 to backup over first ! */
Z	/* copy tail in */
Z	strcat(cmdbuf,tail);
Z	free(history[hiscount]);
Z	history[hiscount] = savestr(cmdbuf);
Z/* $ */ goto charstate;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate histstate
Z */
Z};
Z#line 366 "main.s"
Z
Z
Zpipe:
Z{
Z#line 368 "main.s"
Z	if (inpipe++)
Z	{
Z		inpipe = 1;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	strcat(localbuf," > ");
Z	currname ^= 1;
Z	strcat(localbuf,pipename[currname]);
Z	command(localbuf);
Z	local = localbuf;
Z	setmem(localbuf,sizeof(localbuf),0);
Z/* $ */ goto eatwhitespace;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate pipe
Z */
Z};
Z#line 382 "main.s"
Z
Z
Zeatwhitespace:
Z{
Z#line 384 "main.s"
Z/* strip out leading white space */
Zwhile(isspace(*current))
Z		current++;
Z	if (!*current)
Z/* $ */ goto parserr;
Z	else
Z/* $ */ goto charstate;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate eatwhitespace
Z */
Z};
Z#line 392 "main.s"
Z
Z
Zparserr:
Z{
Z#line 394 "main.s"
Z/* $ */ goto getline;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate parserr
Z */
Z};
Z#line 396 "main.s"
Z
Z
Zdone:
Z{
Z#line 398 "main.s"
Z	j++;	/* next command # */
Z/* $ */ goto getline;
Z
Z#ifndef SNODEBUG
Zgoto badstate;
Z#endif
Z/* 
Z * $endstate done
Z */
Z};
Z#line 401 "main.s"
Z
Z
Z/*
Z * BAD STATE LABEL
Z */
Zbadstate:
Z
Z	fprintf(stderr,"Fallen off end of a state!!!\n");
Z
Z	return -1;
Z
Z/*
Z * TERMINAL STATE LABEL
Z */
Zterminal:
Z
Z	return 0;
Z
Z/*
Z * end of state machine cli
Z */
Z}
Z
Zonintr()
Z{
Z	longjmp(env,-1);
Z}
Z
Zcommand(current)
Z	register char *current;
Z{
Z	extern do_prog();
Z	register int i;
Z	std_save();
Z	if (-1 == (i = findcmd(current)))
Z	{
Z		ctl_brk_setup();
Z		result = _Croot(current,do_prog);
Z		ctl_brk_restore();
Z	}
Z	else
Z	{
Z		if (-1 != setjmp(env))
Z		{
Z			signal(SIGINT,onintr);
Z			result = _Croot(current,commands[i].func);
Z		}
Z		signal(SIGINT,SIG_IGN);
Z	}
Z	std_restore();
Z}
Z
Zchar *
Zntharg(line,index)
Zregister char *line;
Z{
Z	register int i;
Z	static char buf[64];
Z	char *bptr;
Z	for (i = 0; *line;i++)
Z	{
Z		/* find start of arg[i] */
Z		while(*line && isspace(*line))
Z		{
Z			++line;
Z		}
Z		/* if this is start of requested arg, return pointer to it */
Z		if (i == index)
Z		{
Z			bptr = buf;
Z			while(*line && !isspace(*line))
Z				*bptr++ = *line++;
Z			*bptr = '\0';
Z			return buf;
Z		}
Z		/* find end of arg[i] */
Z		while(*line && !isspace(*line))
Z			++line;
Z	}
Z	return NULL;
Z}
Z
Zlastarg(line)
Zregister char *line;
Z{
Z	register int i;
Z
Z	for (i = 0; *line;i++)
Z	{
Z		/* find start of arg[i] */
Z		while(*line && isspace(*line))
Z			++line;
Z		/* find end of arg[i] */
Z		while(*line && !isspace(*line))
Z			++line;
Z	}
Z	return i-1;
Z}
STUNKYFLUFF
set `sum main.c`
if test 41066 != $1
then
echo main.c: Checksum error. Is: $1, should be: 41066.
fi
#
#
echo Extracting main.s:
sed 's/^Z//' >main.s <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <ctype.h>
Z#include <signal.h>
Z#include <fcntl.h>
Z#include <setjmp.h>
Ztypedef struct		/* used to find builtin commands */
Z{
Z	char *cmdname;
Z	int (*func)();
Z} builtin;
Z
Zextern builtin commands[];
Zextern char histerr[];
Zextern int j,hiscount;
Zextern char *history[];
Zextern int histsize;
Zextern int numcmds;
Zstatic int quiet = 0;
Z
Zchar *version = "SHELL VERSION 1.1 Kent Williams";
Z
Zjmp_buf env;
Z
Zchar *pipename[] =
Z{
Z	"\\shtmp1",
Z	"\\shtmp2"
Z};
Z
Zchar cmdbuf[512];
Zint  currname = 0;
Zint result = 0;
Z
Zmain(argc,argv)
Z	char *argv[];
Z{
Z	signal(SIGINT,SIG_IGN);	/* ignore breaks */
Z
Z	quiet = !isatty(0);		/* quiet = batch shell */
Z
Z	/* initialize local environment */
Z	init_env();
Z	cli();
Z	exit(0);
Z}
Z$nodebug		/* turn off state machine debugging */
Z$machine cli getline () 
Z$endargs
Z/* global variables */
Z	int i;
Z	int repeat, state, inpipe = 0;
Z	static char localbuf[256];
Z	static char histbuf[256];
Z	static char tail[256];
Z	int histindex,argindex,takeline;
Z	char *local = localbuf;
Z	char *current,*curr_save;
Z	char *ntharg(), *argptr;
Z	char *savestr();
Z
Z$state getline
Z		/* kill tmp files */
Z		unlink(pipename[0]); unlink(pipename[1]);
Z
Z		hiscount = j % histsize; /* hiscount is current position in history */
Z		if(!quiet)
Z			fprintf(stderr,"%d%% ",j);
Z
Z/*
Z * The following code simply reads a line from standard input.
Z * It is so complicated because when you save the standard stream
Z * files and execute another program/command, standard input is
Z * left in an uncertain state - the FILE stdin seems to be at EOF,
Z * even when standard input is associated with the console, and
Z * cr/lf combinations show up as line terminators, whereas usually
Z * only linefeeds get placed in the input stream.
Z * WHY? beats me.  Something could be wrong with
Z *  1. AZTEC C runtime
Z *  2. PCDOS
Z *  3. Me
Z *  4. All three, or permutations of 1-3 reducto ad absurdum.
Z * All I know is this works
Z */
Z		/* clear command buffer so string read is null terminated */
Z		setmem(cmdbuf,sizeof(cmdbuf),0);
Z		for (current = cmdbuf;;current++)
Z		{
Z			int readresult;
Z			if ((readresult = read(0,current,1)) == 0 ||
Z				readresult == -1)
Z			{
Z				$nextstate terminal
Z			}
Z			if (*current == '\r')
Z			{
Z				if ((readresult = read(0,current,1)) == 0 ||
Z					readresult == -1)
Z				{
Z					$nextstate terminal
Z				}
Z				*current = '\0';
Z				break;
Z			}
Z			else if (*current == '\n')
Z			{
Z				*current = '\0';	/* terminate string */
Z				break;
Z			}
Z		}
Z		current = cmdbuf;	/* point current at start of buffer */
Z/*
Z * end of input weirdness
Z */
Z		/* if we're recycling history strings, free previous one */
Z		if (history[hiscount])
Z			free(history[hiscount]);
Z
Z		/* save current in history array */
Z		history[hiscount] = savestr(current);
Z		/* parse command for compound statements and pipes */
Z		local = localbuf;	/* set pointer to state of buffer */
Z		setmem(localbuf,sizeof(localbuf),0);	/* clear buffer */
Z		$nextstate eatwhitespace 
Z$endstate getline
Z
Z$state charstate
Z	switch(*current)
Z	{
Z	case '\0':
Z		*local = '\0';
Z		current++;
Z		$nextstate emit
Z	case '"' :
Z		*local++ = *current++;
Z		$nextstate doublequotes
Z	case '/' :
Z		*local++ = '\\';
Z		current++;
Z		$nextstate charstate;
Z	case '\'':
Z		*local++ = *current++;
Z		$nextstate singlequotes
Z	case '\\':
Z		*local++ = *++current;
Z		current++;
Z		$nextstate charstate
Z	case ';':
Z		*local = '\0';
Z		current++;
Z		$nextstate compound
Z	case '|':
Z		*local = '\0';
Z		current++;
Z		$nextstate pipe
Z	case '!':
Z		current++;
Z		$nextstate histstate
Z	default:
Z		*local++ = *current++;
Z		$nextstate charstate
Z	}
Z$endstate charstate
Z
Z$state emit
Z	if (inpipe)
Z	{
Z		inpipe = 0;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	command(localbuf);
Z	$nextstate done
Z$endstate emit
Z
Z$state compound
Z	if (inpipe)
Z	{
Z		inpipe = 0;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	command(localbuf);
Z	local = localbuf;
Z	setmem(localbuf,sizeof(localbuf),0);	/* clear buffer */
Z	$nextstate eatwhitespace
Z$endstate compound
Z
Z$state singlequotes
Z	switch (*current)
Z	{
Z	case '\0':
Z		write(2,"No closing quotes!!\r\n",21);
Z		$nextstate parserr
Z	case '\'':
Z		*local++ = *current++;
Z		$nextstate charstate
Z	default:
Z		*local++ = *current++;
Z		$nextstate singlequotes
Z	}
Z$endstate singlequotes
Z
Z$state doublequotes
Z	switch(*current)
Z	{
Z	case '\0':
Z		write(2,"No closing quotes!!\r\n",21);
Z		$nextstate done
Z	case '"':
Z		*local++ = *current++;
Z		$nextstate charstate
Z	default:
Z		*local++ = *current++;
Z		$nextstate doublequotes
Z	}
Z$endstate doublequotes
Z
Z$state histstate
Z	/* handle history substitutions */
Z	setmem(histbuf,sizeof(histbuf),0);	/* clear buffer */
Z
Z	/* save current pointer into command buffer */
Z	curr_save = current;
Z
Z	/* copy command head */
Z	strncpy(histbuf,cmdbuf,(int)(current-cmdbuf)-1);
Z
Z	/* takeline means take all arguments past current one */
Z	takeline = 0;
Z
Z	/* parse history expression */
Z	switch (*current)
Z	{
Z	case '!':	/* last command line */
Z		if (j)	/* special case first time through */
Z		{
Z			histindex = hiscount ? hiscount - 1 : histsize - 1;
Z		}
Z		else
Z		{
Z			/* force error condition */
Z			write(2,histerr,strlen(histerr));
Z			$nextstate parserr
Z		}
Z		current++;	/* point to next */
Z		break;
Z	case '-':		/* negative (relative #) */
Z	/* a particular numbered command */
Z	case '0':
Z	case '1':
Z	case '2':
Z	case '3':
Z	case '4':
Z	case '5':
Z	case '6':
Z	case '7':
Z	case '8':
Z	case '9':
Z		/* repeat numbered command */
Z		repeat = atoi(current);
Z		if (repeat < 0)	/* handle relative addressing */
Z			repeat += j;
Z
Z		/* if command is within range */
Z		if ((j - repeat) <= histsize && repeat < j)
Z		{
Z			histindex = repeat % histsize;
Z		}
Z		else
Z		{
Z			$nextstate parserr
Z		}
Z
Z		/* skip past numeric expression */
Z		while(isdigit(*current)||*current=='-')
Z			++current;
Z		break;
Z	default:
Z		write(2,"Bad history expression\r\n",24);
Z		$nextstate parserr
Z	}
Z	/* look for particular argument substitutions */
Z	switch (*current)
Z	{
Z	/* we want the whole enchilada */
Z	case '\0':
Z	case '\t':
Z	case '\r':
Z	case '\n':
Z	case ' ':
Z		strcat(histbuf,history[histindex]);
Z		break;
Z	case ':':
Z		++current;	/* point past colon */
Z		switch (*current)
Z		{
Z		case '^':
Z			argindex = 1;
Z			++current;
Z			break;
Z		case '0':
Z		case '1':
Z		case '2':
Z		case '3':
Z		case '4':
Z		case '5':
Z		case '6':
Z		case '7':
Z		case '8':
Z		case '9':
Z			/* index of argument */ 
Z			argindex = atoi(current);
Z			while(isdigit(*current))
Z				++current;
Z			if (*current == '*')
Z			{
Z				takeline = 1;
Z				current++;
Z			}
Z			break;
Z		case '$':
Z			argindex = lastarg(history[histindex]);
Z			current++;
Z			break;
Z		case '*':
Z			takeline = 1;	/* take arg 1 through arg n */
Z			argindex = 1;
Z			current++;
Z			break;
Z		default:
Z			$nextstate parserr
Z		}
Z		/* pick up pointer to argument in history we need */
Z		if (takeline == 0)
Z		{
Z			if (NULL == 
Z				(argptr = ntharg(history[histindex],argindex)))
Z			{
Z				$nextstate parserr
Z			}
Z			strcat(histbuf,argptr);
Z		}
Z		else
Z		{
Z			while (NULL !=
Z				(argptr = ntharg(history[histindex],argindex++)))
Z			{
Z				strcat(histbuf,argptr);
Z				strcat(histbuf," ");
Z			}
Z		}
Z	}
Z	/* history substitutions */
Z	/* copy command buffer tail to tail buffer */
Z	strcpy(tail,current);
Z	/* copy histbuf back to cmdbuf */
Z	strcpy(cmdbuf,histbuf);
Z	/* point current at history substitution to continue parsing */
Z	current = --curr_save; /* -1 to backup over first ! */
Z	/* copy tail in */
Z	strcat(cmdbuf,tail);
Z	free(history[hiscount]);
Z	history[hiscount] = savestr(cmdbuf);
Z	$nextstate charstate
Z$endstate histstate
Z
Z$state pipe
Z	if (inpipe++)
Z	{
Z		inpipe = 1;
Z		strcat(localbuf," < ");
Z		strcat(localbuf,pipename[currname]);
Z	}
Z	strcat(localbuf," > ");
Z	currname ^= 1;
Z	strcat(localbuf,pipename[currname]);
Z	command(localbuf);
Z	local = localbuf;
Z	setmem(localbuf,sizeof(localbuf),0);
Z	$nextstate eatwhitespace
Z$endstate pipe
Z
Z$state eatwhitespace
Z/* strip out leading white space */
Zwhile(isspace(*current))
Z		current++;
Z	if (!*current)
Z		$nextstate parserr
Z	else
Z		$nextstate charstate
Z$endstate eatwhitespace
Z
Z$state parserr
Z	$nextstate getline
Z$endstate parserr
Z
Z$state done
Z	j++;	/* next command # */
Z	$nextstate getline
Z$endstate done
Z
Z$endmachine cli
Z
Zonintr()
Z{
Z	longjmp(env,-1);
Z}
Z
Zcommand(current)
Z	register char *current;
Z{
Z	extern do_prog();
Z	register int i;
Z	std_save();
Z	if (-1 == (i = findcmd(current)))
Z	{
Z		ctl_brk_setup();
Z		result = _Croot(current,do_prog);
Z		ctl_brk_restore();
Z	}
Z	else
Z	{
Z		if (-1 != setjmp(env))
Z		{
Z			signal(SIGINT,onintr);
Z			result = _Croot(current,commands[i].func);
Z		}
Z		signal(SIGINT,SIG_IGN);
Z	}
Z	std_restore();
Z}
Z
Zchar *
Zntharg(line,index)
Zregister char *line;
Z{
Z	register int i;
Z	static char buf[64];
Z	char *bptr;
Z	for (i = 0; *line;i++)
Z	{
Z		/* find start of arg[i] */
Z		while(*line && isspace(*line))
Z		{
Z			++line;
Z		}
Z		/* if this is start of requested arg, return pointer to it */
Z		if (i == index)
Z		{
Z			bptr = buf;
Z			while(*line && !isspace(*line))
Z				*bptr++ = *line++;
Z			*bptr = '\0';
Z			return buf;
Z		}
Z		/* find end of arg[i] */
Z		while(*line && !isspace(*line))
Z			++line;
Z	}
Z	return NULL;
Z}
Z
Zlastarg(line)
Zregister char *line;
Z{
Z	register int i;
Z
Z	for (i = 0; *line;i++)
Z	{
Z		/* find start of arg[i] */
Z		while(*line && isspace(*line))
Z			++line;
Z		/* find end of arg[i] */
Z		while(*line && !isspace(*line))
Z			++line;
Z	}
Z	return i-1;
Z}
STUNKYFLUFF
set `sum main.s`
if test 47403 != $1
then
echo main.s: Checksum error. Is: $1, should be: 47403.
fi
#
#
echo Extracting makefile:
sed 's/^Z//' >makefile <<\STUNKYFLUFF
Z# home for all commands
ZBINDIR=\\bin\\
Z# small model library path
ZCLIB=-l/clibs/c
Z
Z# source files for shell
Zshsrc=main.c cd.c cp.c doprog.c fexecvp.c more.c cmds.c chmod.c y.c fexec.asm \
Zinvalid.c ls.c pushd.c fgrep.c md.c mv.c pwd.c rm.c crlf.c drive.c dump2.c \
Zrmdir.c savestr.c stdsave.c touch.c _croot.c env.c fexecv.c cat.c echo.c \
Zcmdlist.c ctlbrk.asm mvcp.c
Z
Z# object files for shell
Zshobj=main.o cd.o cp.o doprog.o fexecvp.o more.o cmds.o chmod.o y.o fexec.o \
Zinvalid.o ls.o pushd.o fgrep.o md.o mv.o pwd.o rm.o crlf.o drive.o dump2.o \
Zrmdir.o savestr.o stdsave.o touch.o _croot.o env.o fexecv.o cat.o echo.o \
Zcmdlist.o ctlbrk.o mvcp.o
Z
Zmain.c : main.s
Z	statecom main
Z
Z#
Z# make shell - check currency of all percursors, link and then move to
Z# bin directory
Z#
Zshell.exe : $(shobj) 
Z	ln -t -o $@ -f shell.lnk
Z	\\atron\\aztoat <shell.sym >shell.map
Z	chmod +w $(BINDIR)$@
Z	mv $@ $(BINDIR)$@
Z	chmod -w $(BINDIR)$@
Z
Z#
Z# make entries to produce stand-alone versions of shell builtins
Z#
Zchmod.com : 
Z	cc -dMAIN $*
Z	ln -o $@ $*.o -l/clibs/c
Z	rm $*.o
Z	chmod +w $(BINDIR)chmod.com
Z	mv chmod.com $(BINDIR)chmod.com
Z	chmod -w $(BINDIR)chmod.com
Z
Zmore.com : 
Z	cc -dMAIN $*
Z	ln -o $@ $*.o -l/clibs/s -l/clibs/c
Z	rm $*.o
Z	chmod +w $(BINDIR)$@
Z	mv $@ $(BINDIR)$@
Z	chmod -w $(BINDIR)$@
Z
Zmv.com : croot.o savestr.o mvcp.o
Z	cc -dMAIN $*
Z	ln -o $@ $*.o savestr.o croot.o mvcp.o  -l/clibs/c
Z	rm $*.o
Z	chmod +w $(BINDIR)$@
Z	mv $@ $(BINDIR)$@
Z	chmod -w $(BINDIR)$@
Z
Zcp.com : croot.o savestr.o mvcp.o
Z	cc -dMAIN $*
Z	ln -o $@ $*.o savestr.o croot.o  mvcp.o -l/clibs/c
Z	rm $*.o
Z	chmod +w $(BINDIR)$@
Z	mv $@ $(BINDIR)$@
Z	chmod -w $(BINDIR)$@
Z
Zls.com : 
Z	cc -dMAIN $*
Z	ln -o $@ $*.o -l/clibs/c
Z	rm $*.o
Z	chmod +w $(BINDIR)$@
Z	mv $@ $(BINDIR)$@
Z	chmod -w $(BINDIR)$@
Zeditall :
Z	z $(shsrc)
Zclean :
Z	rm *.o *.bak *.sym
STUNKYFLUFF
set `sum makefile`
if test 36319 != $1
then
echo makefile: Checksum error. Is: $1, should be: 36319.
fi
#
#
echo Extracting md.c:
sed 's/^Z//' >md.c <<\STUNKYFLUFF
Zmd(argc,argv)
Z	char *argv[];
Z{
Z	if (-1 == mkdir(*(++argv)))
Z	{
Z		perror("mkdir");
Z		return -1;
Z	}
Z	return 0;
Z}
STUNKYFLUFF
set `sum md.c`
if test 21756 != $1
then
echo md.c: Checksum error. Is: $1, should be: 21756.
fi
#
#
echo Extracting more.c:
sed 's/^Z//' >more.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <sgtty.h>
Z#include <debug.h>
Z#include <ctype.h>
Z#include <setjmp.h>
Z#include <signal.h>
Zvoid (*signal())();
Zvoid (*moresig)();
ZFILE *fdopen();
Z
Zlong int size, position;
Zchar filename[30];
Zchar isaconsole = '\0';
Zchar isapipe = '\0';
Zint tabsize = 8;
Z#define STDIN 0
Z#define STDOUT 1
Z#define STDERR 2
Z#define ESC '\033'
Z#define IBMPC			/* display code dependent on PC hardware/bios */
Z#ifdef IBMPC
Zextern void scr_putc();
Z#endif
Zvoid std_out(c)
Z{
Z	write(1,&c,1);
Z}
Zvoid (*output)();
Z#ifdef MAIN
Zcrlf()
Z{
Z	write(2,"\r\n",2);
Z}
Z#endif
Zjmp_buf moreenv;
Zmoreintr()
Z{
Z	signal(SIGINT,SIG_IGN);	/* ignore signals until we finish putting stuff
Z	                           back in order */
Z	longjmp(moreenv,-1);
Z}
Z#ifdef MAIN
Zmain
Z#else
Zmore
Z#endif
Z(argc,argv)
Z	int argc;
Z	char *argv[];
Z{
Z	FILE *fp, *fopen();
Z	long ftell();
Z	long int i;
Z	long int fsize();
Z
Z	if (-1 == setjmp(moreenv))
Z	{
Z		write(2,"Interrupted\r\n",13);
Z		fclose(fp);
Z		signal(SIGINT,moresig);
Z		return -1;
Z	}
Z
Z	moresig = signal(SIGINT,moreintr);
Z	isaconsole = isatty(STDOUT);
Z	isapipe = !isatty(STDIN);
Z#ifdef IBMPC
Z	if (isaconsole)
Z	{
Z		output = scr_putc;
Z		scr_echo(0);
Z	}
Z	else
Z#endif
Z		output = std_out;
Z	if ( (*(++argv))[0] == '-' && isdigit((*argv)[1]) )
Z	{
Z		tabsize = atoi( (*argv+1) );
Z		--argc;
Z	} else
Z	{
Z		--argv;
Z	}
Z	if (argc == 1)
Z	{
Z		if (NULL == (fp = fdopen(0,"r")))
Z		{
Z			return -1;
Z		}
Z		display(fp);
Z		crlf();
Z		return 0;
Z	}
Z	while(--argc) 
Z	{
Z		if (NULL == (fp = fopen(*(++argv),"r")) )
Z		{
Z			fprintf(stderr,"more - can't open %s\n",*argv);
Z			continue;
Z		}
Z		strncpy(filename,*argv,30);
Z		if (filename[29])
Z			filename[29] = '\0';
Z		if (!isapipe)
Z			size = fsize(fp);
Z		position = 0;
Z		if (-2 == display(fp))
Z			argc = 0;		/* force completion of command */
Z		fclose(fp);
Z	}
Zbugout:
Z#ifdef IBMPC
Z	if (isaconsole)
Z		scr_echo(0);
Z#endif
Z	crlf();
Z	tabsize = 8;
Z	isaconsole = isapipe = '\0';
Z	signal(SIGINT,moresig);
Z	return 0;
Z}
Z
Zlong fsize(fp) 
Z	FILE *fp;
Z{
Z	long ftell();
Z	long position, last;
Z	position = ftell(fp);
Z	if (-1 == fseek(fp,0L,2))
Z	{
Z		fprintf(stderr,"more - error on fseek\n");
Z	}
Z	last = (ftell(fp));
Z	fseek(fp,position,0);
Z	return last;
Z}
Z#define LBUFSIZE 160
Zchar linebuffer[LBUFSIZE];
Zint lines;
Z
Zdisplay(fp)
Z	FILE *fp;
Z{
Z	FILE *fgets();
Z	long ftell();
Z	char c;
Z	lines = 1;
Z	while ( NULL != fgets(linebuffer,LBUFSIZE,fp))
Z	{
Z		if (isaconsole)
Z		{
Z			if (lines == 1)		/* top of display */
Z			{
Z#ifdef IBMPC
Z				scr_clear();
Z#else
Z/* ansi terminal screen clear */
Z				printf("%c[2J",ESC);	/* clear display */
Z#endif
Z			}
Z		}
Z		lines += localputs(linebuffer);
Z		if (isaconsole)
Z		{
Z			if (lines >=  24)		/* bottome of display */
Z			{
Z				char tst;
Z				position = ftell(fp);
Z#ifdef IBMPC
Z				scr_curs(24,0);
Z#else
Z/* ansi terminal positioning */
Z				printf("%c[25;1H%c[7m",ESC,ESC);
Z#endif
Z				if (!isapipe)
Z#ifdef IBMPC
Z					scr_printf(
Z#else
Z					fprintf(stderr,
Z#endif
Z				"%s - %ld bytes - %d%% displayed - <ESC> = skip to next file",
Z					filename,size,percent(position,size) );
Z				else
Z#ifdef IBMPC
Z					scr_printf(
Z#else
Z					fprintf(stderr,
Z#endif
Z									"-more-");
Z				switch (bdos(7,0,0))	/* get a character no echo */
Z				{
Z				case ESC :
Z					return 0;
Z				case 3 :
Z					return -2;
Z				default:
Z					break;
Z				}
Z
Z				lines = 1;
Z			}
Z		}
Z	}
Z	if (isaconsole)
Z	{
Z		if (lines != 1)		/* bottome of display */
Z		{
Z#ifdef IBMPC
Z			scr_curs(24,0);
Z#else
Z/* ansi terminal positioning */
Z				printf("%c[25;1H%c[7m",ESC,ESC);
Z#endif
Z			if (!isapipe)
Z			{
Z				position = ftell(fp);
Z#ifdef IBMPC
Z				scr_printf(
Z#else
Z				fprintf(stderr,
Z#endif
Z						"%s - %ld characters - %d%% displayed",
Z					filename,size,percent(position,size) );
Z			}
Z			else
Z#ifdef IBMPC
Z				scr_printf(
Z#else
Z				fprintf(stderr,
Z#endif
Z								"-done-");
Z			bdos(7,0,0); /* console input no echo */
Z			lines = 1;
Z		}
Z	}
Z}
Z
Zpercent(x,y)
Z	long int x,y;
Z{	/* returns integer percentage of x into y */
Z#ifdef FLOAT
Z	float xf,yf;
Z	xf = x; yf = y;
Z	x = ((xf/yf)*100);
Z#endif
Z	x *= 100;
Z	if (y)
Z		x /= y;
Z	else
Z		x = 100;
Z	return (x);
Z}
Z
Zlocalputs(lb)
Z	register char *lb;
Z{
Z	int lines, pos, tabstop;
Z	lines = 1;
Z	pos = 0;
Z	while (*lb)
Z	{
Z		switch (*lb)
Z		{
Z		case '\t':
Z			tabstop = pos + (tabsize - (pos % tabsize));	
Z			for (;pos <= tabstop; pos++)
Z				(*output)(' ');
Z			break;
Z		case '\n':
Z			(*output)('\r');
Z		default:
Z			(*output)(*lb);
Z			pos++;
Z		}
Z		if (pos == 79)
Z		{
Z			pos = 1;
Z			(*output)('\r');
Z			(*output)('\n');
Z			++lines;
Z		} else if (pos > 79)
Z		{
Z			pos -= 80;
Z			++lines;
Z		}
Z		++lb;
Z	}
Z	return lines;
Z}
Z
Z#ifdef IBMPC
Zscr_printf(fmt,args)
Zchar *fmt; unsigned args;
Z{
Z	format(scr_putc,fmt,&args);
Z}
Z#endif
STUNKYFLUFF
set `sum more.c`
if test 34929 != $1
then
echo more.c: Checksum error. Is: $1, should be: 34929.
fi
#
#
echo Extracting mv.c:
sed 's/^Z//' >mv.c <<\STUNKYFLUFF
Z/* :ts=2 */
Z#include <stdio.h>
Zchar *savestr();
Z#ifndef MAIN
Zextern char *fname_part(),*savestr();
Zextern char *me;
Z#else
Zchar *me;
Z#endif
Z/* mv.c - implements a version of UNIX mv */
Z#ifdef MAIN
Zmain
Z#else
Zmv
Z#endif
Z(argc,argv)
Z	int argc;
Z	register char *argv[];
Z{
Z	static char *usage = "mv : usage mv file1 [file2 . . fileN] target\r\n";
Z	static char target_name[128];
Z	char target[128],*fname_part();
Z	register int i;
Z	int len;
Z#ifndef MAIN
Z	me = argv[0];	/* referenced in routines common with cp.c */
Z#endif
Z	if (argc < 3)
Z	{
Z		write(2,usage,strlen(usage));
Z		return(-1);
Z	}
Z	strcpy(target, argv[argc-1]);
Z	/* kill trailing backslashes */
Z	if (target[i = strlen(target) - 1] == '\\')
Z		target[i] = '\0';
Z	if (argc == 3)
Z	{
Z		/* if the target doesn't exist and it's not a directory then rename */
Z		if (!filep(target))
Z		{
Z			if (!dirp(target))
Z			{
Z				fprintf(stderr,"rename : moving %s to %s\n"
Z				,argv[1],argv[2]);
Z				rename(argv[1],argv[2]);
Z			}
Z			else
Z			{
Z			/* if the target is a directory copy to same name that directory */
Z				strcpy(target_name,target);
Z				if (target_name[(len = strlen(target_name))-1] != '\\')
Z				{
Z					target_name[len = strlen(target_name)] = '\\';
Z					target_name[len+1] = '\0';
Z				}
Z				strcat(target_name,fname_part(argv[1]));
Z#ifdef DEBUG
Z				fprintf(stderr,"interdirectory copy same name : moving %s to %s\n"
Z#else
Z				fprintf(stderr,"moving %s to %s\n"
Z#endif
Z				,argv[1],target_name);
Z				if (-1 != maybecopy(target_name,argv[1]))
Z					unlink(argv[1]);
Z			}
Z		}
Z		else
Z		{
Z		/* target exists , and isn't a directory */
Z			char *tpath,*spath;
Z			char tpathsaved = 0,spathsaved = 0;
Z			char *path_part();
Z			/* find path parts of source and target */
Z			if (tpath = path_part(target))
Z			{
Z				tpath = savestr(tpath);
Z				tpathsaved++;
Z			}
Z			else
Z				tpath = ".";	/* current directory */
Z			if (spath = path_part(argv[1]))
Z			{
Z				spath = savestr(spath);
Z				spathsaved++;
Z			}
Z			else
Z				spath = ".";	/* current directory */
Z			if (0 == strcmp(tpath,spath))
Z			{
Z			/* if source and target are in the same directory */
Z#ifdef DEBUG
Z				fprintf(stderr,"intradirectory delete then rename : moving %s to %s\n"
Z#else
Z				fprintf(stderr,"moving %s to %s\n"
Z#endif
Z				,argv[1],target);
Z				unlink(target);
Z				rename(argv[1],target);
Z			}
Z			else
Z			{
Z#ifdef DEBUG
Z				fprintf(stderr,"interdirectory file to file: moving %s to %s\n"
Z#else
Z				fprintf(stderr,"moving %s to %s\n"
Z#endif
Z				,argv[1],target);
Z				if (-1 != maybecopy(target,argv[1]))
Z					unlink(argv[1]);
Z			}
Z			if (tpathsaved)
Z				free(tpath);
Z			if (spathsaved)
Z				free(spath);
Z		}
Z		return(0);
Z	}
Z	/* handle special case of a drive designation */
Z	if (target[(i = strlen(target))-1] != ':')
Z		if (!dirp(target))
Z		{
Z			fprintf(stderr,"mv : %s isn't a directory\n",target);
Z			return(-1);
Z		}
Z	for (i = 1; i < argc-1; i++)
Z	{
Z		strcpy(target_name,target);
Z		if (target_name[(len = strlen(target_name))-1] != '\\')
Z		{
Z			target_name[len = strlen(target_name)] = '\\';
Z			target_name[len+1] = '\0';
Z		}
Z		strcat(target_name,fname_part(argv[i]));
Z		if (!filep(argv[i]))
Z		{
Z			fprintf(stderr,"mv : %s isn't a file\n",argv[i]);
Z			continue;
Z		}
Z		fprintf(stderr,"moving %s to %s\n",argv[i],target_name);
Z		if (-1 != maybecopy(target_name,argv[i]))
Z			unlink(argv[i]);
Z	}
Z	return 0;
Z}
Z
Zmaybecopy(target,source)
Z	char *target,*source;
Z{
Z	char *drive_part();
Z	static char targetdrive[3], sourcedrive[3];
Z	strcpy(targetdrive,drive_part(target));
Z	strcpy(sourcedrive,drive_part(source));
Z	if (0 == strcmp(targetdrive,sourcedrive))
Z	{
Z		unlink(target);
Z		rename(source,target);
Z		return 0;
Z	}
Z	return (filecopy(target,source));
Z}
STUNKYFLUFF
set `sum mv.c`
if test 37323 != $1
then
echo mv.c: Checksum error. Is: $1, should be: 37323.
fi
#
#
echo Extracting mvcp.c:
sed 's/^Z//' >mvcp.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z/*
Z * mvcp.c - routines common to mv and cp
Z */
Z#include <fcntl.h>
Zchar buffer[BUFSIZ*16];
Zextern char *me;
Zfilecopy(target,source)
Zchar *target,*source;
Z{
Z    int t,s,r;
Z    if (-1 == (s = open(source,O_RDONLY)))
Z    {
Z		fprintf(stderr,"%s : can't open %s\n",me,source);
Z		return(-1);
Z    }
Z    if (-1 == (t = open(target,O_TRUNC)))
Z    {
Z		fprintf(stderr,"%s : can't open %s\n",me,target);
Z		return(-1);
Z    }
Z    while(0 != (r = read(s,buffer,BUFSIZ*16)) && r != -1)
Z    {
Z		if(-1 == write(t,buffer,r))
Z		{
Z		    fprintf(stderr,"%s : error writing %s\n",me,target);
Z		    return(-1);
Z		}
Z    }
Z    close(t); 
Z    close(s);
Z	return (0);
Z}
Z
Z#include <errno.h>
Ztypedef struct
Z{
Z    char dos_reserved[21];
Z    char attribute;
Z    unsigned file_time;
Z    unsigned file_date;
Z    long file_size;
Z    char file_name[13];
Z} 
Zfcb;
Zfcb dir;
Z
Zdirp(s)
Zchar *s;
Z{
Z	register int junk1,junk2;
Z	/* handle all of the stupid special cases */
Z	if ((s[1] == ':' && s[2] == '\0')	/* root directory on a drive */
Z		|| (s[1] == '\0')				/* root directory default drive */
Z	)
Z	{
Z		return 1;
Z	}
Z	if (0 == strcmp(s,".."))			/* parent of this directory */
Z	{
Z		int returnval;
Z		char *current,*parent;
Z		current = getcwd(NULL,64);
Z		if (-1 == chdir(s)) /* go to parent */
Z			returnval = 0;
Z		else
Z			returnval = 1;
Z		parent = getcwd(NULL,64);
Z		chdir(current);
Z		free(current); free(parent);
Z		return returnval;
Z	}
Z    /* set the disk transfer address */
Z    bdos(0x1A,&dir);
Z    /* do a search first for the directory path */
Z    return (bdos(0x4E,s,0x10) == 0 && bdos(0x4E,s,0) != 0);
Z}
Z
Zfilep(s)
Zchar *s;
Z{
Z    /* set the disk transfer address */
Z    bdos(0x1A,&dir);
Z    /* do a search first for the directory path */
Z    return bdos(0x4E,s,0) == 0;
Z}
Zchar *fname_part(s)
Zregister char *s;
Z{
Z    register char *r; 
Z    char *rindex();
Z    if (r = rindex(s,'\\'))
Z	{
Z		return r+1;
Z	}
Z    if (r = rindex(s,':'))
Z	{
Z		return r+1;
Z	}
Z    return s;
Z}
Zchar *path_part(s)
Zregister char *s;
Z{
Z	static char buffer[64];
Z    register char *r; 
Z    char *rindex();
Z	strcpy(buffer,s);	/* copy string */
Z	if (r = rindex(buffer,'\\'))
Z	{
Z		*++r = '\0';
Z		return buffer;
Z	}
Z	if (r = rindex(buffer,':'))
Z	{
Z		*++r = '\0';
Z		return buffer;
Z	}
Z	return NULL;
Z}
Zchar *drive_part(s)
Zregister char *s;
Z{
Z	static char buffer[64];
Z    register char *r; 
Z    char *rindex();
Z	strcpy(buffer,s);	/* copy string */
Z	if (buffer[1] == ':')
Z	{
Z		buffer[2] = '\0';
Z		return buffer;
Z	}
Z	return NULL;
Z}
STUNKYFLUFF
set `sum mvcp.c`
if test 16416 != $1
then
echo mvcp.c: Checksum error. Is: $1, should be: 16416.
fi
#
#
echo Extracting pushd.c:
sed 's/^Z//' >pushd.c <<\STUNKYFLUFF
Z#define NULL (void *)0
Z
Zstatic char *dirstack[10];
Zstatic int dsp = -1;
Z
Zpushd(argc,argv)
Zchar *argv[];
Z{
Z	char *getcwd(), *savestr();
Z	static char *usage = "usage : pushd newdir";
Z	static char *pusherr = "pushd : dir stack overflow";
Z	char dirbuf[64];
Z	if (argc == 1)
Z	{
Z		write(2,usage,strlen(usage));
Z		crlf();
Z		return -1;
Z	}
Z	if (NULL ==  getcwd(&dirbuf[1],64))
Z	{
Z		perror("pushd");
Z		return -1;
Z	}
Z	if (++dsp == 10)
Z	{
Z		write(2,pusherr,strlen(pusherr));
Z		crlf();
Z		return -1;
Z	}
Z	dirbuf[0] = '\\';
Z	if (-1 == chdir(*(++argv)))
Z	{
Z		--dsp;
Z		perror("pushd");
Z		return -1;
Z	}
Z	dirstack[dsp] = savestr(dirbuf);
Z	return 0;
Z}
Z
Zpopd()
Z{
Z	register int returnval = 0;
Z	static char *poperr = "popd : dir stack underflow";
Z	if (dsp == -1)
Z	{
Z		write(2,poperr,strlen(poperr));
Z		crlf();
Z		return -1;
Z	}
Z	if (-1 == chdir(dirstack[dsp]))
Z	{
Z		perror("popd");
Z		write(2,dirstack[dsp],strlen(dirstack[dsp]));
Z		crlf();
Z		returnval = -1;
Z	}
Z	free(dirstack[dsp--]);
Z	return returnval;
Z}
STUNKYFLUFF
set `sum pushd.c`
if test 22547 != $1
then
echo pushd.c: Checksum error. Is: $1, should be: 22547.
fi
#
#
echo Extracting pwd.c:
sed 's/^Z//' >pwd.c <<\STUNKYFLUFF
Z#define NULL (void *)0
Zpwd(argc,argv)
Zchar *argv[];
Z{
Z	char *getcwd();
Z	register char *dir;
Z	if (!(dir = getcwd(NULL,256)))
Z	{
Z		perror("pwd");
Z		return -1;
Z	}
Z	write(1,"\\",1);
Z	write(1,dir,strlen(dir));
Z	crlf();
Z	if (dir)
Z		free(dir);
Z	return 0;
Z}
STUNKYFLUFF
set `sum pwd.c`
if test 11133 != $1
then
echo pwd.c: Checksum error. Is: $1, should be: 11133.
fi
#
#
echo Extracting rm.c:
sed 's/^Z//' >rm.c <<\STUNKYFLUFF
Z#include <stdio.h>
Zrm(argc,argv)
Z	int argc;
Z	char *argv[];
Z{
Z	extern int _echo;
Z	int oldecho = _echo;
Z	char ask = 0;
Z
Z	if (argv[1][0] == '-' && toupper(argv[1][1]) == 'Q')
Z	{
Z		ask++;
Z		_echo = 1;
Z		++argv; --argc;
Z	}
Z	while(--argc) 
Z	{
Z		++argv;
Z		if (ask)
Z		{
Z			fprintf(stderr,"delete %s? (y or n) : ",*argv);
Z			if (toupper(scr_getc()) != 'Y')
Z			{
Z				write(2,"\r\n",2);
Z				continue;
Z			}
Z			write(2,"\r\n",2);
Z		}
Z		if (-1 == unlink(*(argv)))
Z		{
Z			perror("rm");
Z		}
Z	}
Z	_echo = oldecho;
Z}
Z
Z
STUNKYFLUFF
set `sum rm.c`
if test 00071 != $1
then
echo rm.c: Checksum error. Is: $1, should be: 00071.
fi
#
#
echo Extracting rmdir.c:
sed 's/^Z//' >rmdir.c <<\STUNKYFLUFF
Z#include <stdio.h>
Zrd(argc,argv)
Zchar *argv[];
Z{
Z	static char *usage = "usage : rmdir directory";
Z	if (argc == 1)
Z		write(2,usage,strlen(usage));
Z	if (-1 == rmdir(*(++argv)))
Z	{
Z		perror("rmdir");
Z		return -1;
Z	}
Z	return 0;
Z}
STUNKYFLUFF
set `sum rmdir.c`
if test 22459 != $1
then
echo rmdir.c: Checksum error. Is: $1, should be: 22459.
fi
#
#
echo Extracting savestr.c:
sed 's/^Z//' >savestr.c <<\STUNKYFLUFF
Z#define NULL (void *)0
Zchar *
Zsavestr(s)
Z	register char *s;
Z{
Z	register char *r,*malloc();
Z	/* squirrel away matched file name */
Z	if (NULL == (r = malloc(strlen(s)+1)))
Z		abort();
Z	strcpy(r,s);
Z	r[strlen(s)] = '\0';
Z	return r;
Z}
STUNKYFLUFF
set `sum savestr.c`
if test 32164 != $1
then
echo savestr.c: Checksum error. Is: $1, should be: 32164.
fi
#
#
echo Extracting shell.lnk:
sed 's/^Z//' >shell.lnk <<\STUNKYFLUFF
Zmain.o cd.o cp.o doprog.o fexecvp.o more.o
Zinvalid.o ls.o pushd.o fgrep.o env.o cmds.o chmod.o
Zmd.o mv.o pwd.o rm.o crlf.o drive.o fexecv.o
Zrmdir.o savestr.o stdsave.o touch.o dump2.o mvcp.o
Z_croot.o cat.o echo.o y.o fexec.o cmdlist.o ctlbrk.o
Z/clibs/c.lib /clibs/s.lib
STUNKYFLUFF
set `sum shell.lnk`
if test 41375 != $1
then
echo shell.lnk: Checksum error. Is: $1, should be: 41375.
fi
#
#
echo Extracting ssbrk.asm:
sed 's/^Z//' >ssbrk.asm <<\STUNKYFLUFF
Z; :ts=8
Z;Copyright (C) 1983 by Manx Software Systems
Z	include lmacros.h
Zdataseg	segment	word public 'data'
Z	extrn	$MEMRY:word
Z	extrn	_mbot_:word, _sbot_:word
Z	extrn	_mtop_:word
Z	extrn	errno_:word
Z	extrn	_STKLOW_:word
Z	extrn	_PSP_:word
Zdataseg	ends
Z	assume	ds:dataseg
Z;
Z; sbrk(size): return address of current top & bump by size bytes
Z;
Z	procdef	sbrk,<<siz,word>>
Z	push	di
Z	mov	ax,siz
Z	mov	di,$MEMRY
Z	add	ax,di
Z	push	ax
Z	call	brk_
Z	pop	cx
Z	test	ax,ax
Z	jnz	brk_error
Z	mov	ax,di		;return original value of the break
Zbrk_error:
Z	pop	di
Z	test	ax,ax		;set flags for C
Z	pret
Z	pend	sbrk
Z;
Z; brk(addr):	set current top address to addr
Z;		returns 0 if ok, -1 if error
Z;
Z	procdef brk,<<addr,word>>
Z	mov	ax,addr
Z	inc	ax
Z	and	al,-2
Z	cmp	ax,_mbot_
Z	jb	brk_ov
Z	mov	bx,_STKLOW_
Z	cmp	bx,0
Z	jnz	abovestk
Z	cmp	ax,_sbot_
Z	jae	brk_ov
Z	mov	bx,sp
Z	cmp	ax,bx
Z	jae	brk_ov
Zbrk_ok2:
Z	mov	$MEMRY,ax	;new value is good so save it away
Z	sub	ax,ax
Z	pret
Z;heap is above stack
Zabovestk:
Z	cmp	ax,_mtop_
Z	ja	getstore
Z	cmp	ax,$MEMRY
Z	ja	brk_ok2
Z;			going to do a SETBLOCK call
Zgetstore:
Z	push	ax
Z	mov	bx,ax
Z	mov 	cx,4
Z	shr	bx,cl
Z	and	bx,0fffh
Z	add	bx,65		;bump to nearest 1k increment
Z	and	bx,0ffc0h	;and round
Z	push	bx
Z	push	es
Z	mov	cx,ds
Z	add	bx,cx		;get actual paragraph address
Z	mov	es,_PSP_
Z	sub	bx,_PSP_
Z	mov	ah,04ah
Z	int	21h		;SETBLOCK
Z	pop	es
Z	pop	bx
Z	pop	ax
Z	jc	brk_ov		; couldn't do it, so punt
Z	mov	$MEMRY,ax
Z	test	bx,0e000h
Z	jnz	brk_ov
Z	mov	cx,4
Z	shl	bx,cl
Z	mov	_mtop_,bx
Z	sub	ax,ax
Z	pret
Z; invalid request
Zbrk_ov:
Z	mov	errno_,-4
Z	mov	ax,-1
Z	test	ax,ax
Z	pret
Z	pend	brk
Z;
Z; rsvstk(size):		set safety margin for stack
Z;			this will make sure that at least size
Z;			bytes of stack below the current level remain.
Z;
Z	procdef	rsvstk,<<stksize,word>>
Z	mov	ax,sp
Z	sub	ax,stksize
Z	mov	_sbot_,ax
Z	pret
Z	pend	rsvstk
Z	finish
Z	end
STUNKYFLUFF
set `sum ssbrk.asm`
if test 58681 != $1
then
echo ssbrk.asm: Checksum error. Is: $1, should be: 58681.
fi
#
#
echo Extracting stdsave.c:
sed 's/^Z//' >stdsave.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <fcntl.h>
Z
Zint console;
Zint in,out;	/* old standard input and standard output */
Zstd_save()
Z{
Z	if (-1 == (console = open("con",O_RDWR)))
Z	{
Z		perror("sh : can't open console");
Z		return -1;
Z	}
Z	in = dup(0);
Z	out = dup(1);
Z	fdup(console,0);
Z	fdup(console,1);
Z	fdup(console,2);
Z	return 0;
Z}
Zvoid 
Zstd_restore()
Z{
Z	fdup(in,0);
Z	fdup(out,1);
Z	close(console);
Z	close(in);
Z	close(out);
Z}
STUNKYFLUFF
set `sum stdsave.c`
if test 61619 != $1
then
echo stdsave.c: Checksum error. Is: $1, should be: 61619.
fi
#
#
echo Extracting stksiz.c:
sed 's/^Z//' >stksiz.c <<\STUNKYFLUFF
Zint _STKSIZ = 8192/16;  /* (in paragraphs) */
Zint _HEAPSIZ = 32768/16; /* (in paragraphs) */
Zint _STKLOW = 1;		/* default is stack above heap (small only) */
STUNKYFLUFF
set `sum stksiz.c`
if test 11181 != $1
then
echo stksiz.c: Checksum error. Is: $1, should be: 11181.
fi
#
#
echo Extracting touch.c:
sed 's/^Z//' >touch.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z
Ztouch(argc,argv)
Z	char *argv[];
Z{
Z	while(--argc)
Z		utime(*(++argv),NULL);
Z}
STUNKYFLUFF
set `sum touch.c`
if test 31978 != $1
then
echo touch.c: Checksum error. Is: $1, should be: 31978.
fi
#
#
echo Extracting y.c:
sed 's/^Z//' >y.c <<\STUNKYFLUFF
Z#include <stdio.h>
Z#include <signal.h>
Z#include <setjmp.h>
Z
Z/* y and t
Z * y reads the standard input to standard output, then invokes cat
Z * to put one or more files to standard output
Z * tee copies standard input to output and puts a copy
Z * into a file specified on the command line
Z */
Z
Zvoid (*signal())();
Zvoid (*teesig)();
Zjmp_buf y_env;
Zstatic FILE *in,*out;
Z
ZFILE *fopen(),*fdopen();
Z
Zvoid y_intr()
Z{
Z	signal(SIGINT,SIG_IGN);
Z	longjmp(y_env,-1);
Z}
Z
Zy(argc,argv)
Z	int argc;
Z	char *argv[];
Z{
Z	register int c;
Z
Z	/* handle interrupts */
Z	if (-1 == setjmp(y_env))
Z	{
Z		static char *intmsg = "Interrupted\r\n";
Z		write(2,intmsg,strlen(intmsg));
Z		fclose(in);
Z		fclose(out);
Z		signal(SIGINT,teesig);
Z		return -1;
Z	}
Z
Z	/* set signal catcher */
Z	teesig = signal(SIGINT,y_intr);
Z
Z	if (NULL == (in = fdopen(0,"r")))
Z	{
Z		fprintf(stderr,"can't open stdin\n");
Z	};
Z	if (NULL == (out = fdopen(1,"w")))
Z	{
Z		fprintf(stderr,"can't open stdout\n");
Z	};
Z
Z	while(EOF != (c = agetc(in)))
Z		aputc(c,out);
Z	if (argc > 1)
Z		cat(argc,argv);
Z	fclose(in);
Z	fclose(out);
Z	signal(SIGINT,teesig);
Z	return 0;
Z}
Z
Zjmp_buf t_env;
Z
Zvoid t_intr()
Z{
Z	signal(SIGINT,SIG_IGN);
Z	longjmp(t_env,-1);
Z}
Z
Zt(argc,argv)
Z	int argc;
Z	char *argv[];
Z{
Z	register int c;
Z	register FILE *tfile;
Z	FILE *fopen();
Z
Z	/* handle interrupts */
Z	if (-1 == setjmp(t_env))
Z	{
Z		static char *intmsg = "Interrupted\r\n";
Z		write(2,intmsg,strlen(intmsg));
Z		fclose (tfile);
Z		fclose(in);
Z		fclose(out);
Z		signal(SIGINT,teesig);
Z		return -1;
Z	}
Z
Z	/* set signal catcher */
Z	teesig = signal(SIGINT,t_intr);
Z
Z	if (NULL == (tfile = fopen(*(++argv),"w")))
Z	{
Z		fprintf(stderr,"can't open %s\n",*argv);
Z	};
Z	if (NULL == (in = fdopen(0,"r")))
Z	{
Z		fprintf(stderr,"can't open stdin\n");
Z	};
Z	if (NULL == (out = fdopen(1,"w")))
Z	{
Z		fprintf(stderr,"can't open stdout\n");
Z	};
Z
Z	while(EOF != (c = agetc(in)))
Z	{
Z		aputc(c,out);
Z		if (tfile)
Z			aputc(c,tfile);
Z	}
Z	fclose(in);
Z	fclose(out);
Z	fclose(tfile);
Z	signal(SIGINT,teesig);
Z	return 0;
Z}
STUNKYFLUFF
set `sum y.c`
if test 44898 != $1
then
echo y.c: Checksum error. Is: $1, should be: 44898.
fi
echo ALL DONE BUNKY!
exit 0
-- 
                                                                          --
                .^.                        michael regoli 
                /|\        ...ihnp4!inuxc!iuvax!isrnix!mr 
               '|!|`                     <mr@isrnix.UUCP>