[comp.sources.amiga] v91i091: CShell 5.10 - alternative command interface, Part04/06

amiga-request@ab20.larc.nasa.gov (Amiga Sources/Binaries Moderator) (04/17/91)

Submitted-by: umueller@iiic.ethz.ch
Posting-number: Volume 91, Issue 091
Archive-name: shells/cshell-5.10/part04

#!/bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 4 (of 6)."
# Contents:  comm3.c csh.doc.aa
# Wrapped by tadguy@ab20 on Tue Apr 16 15:34:35 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'comm3.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'comm3.c'\"
else
echo shar: Extracting \"'comm3.c'\" \(28599 characters\)
sed "s/^X//" >'comm3.c' <<'END_OF_FILE'
X/*
X * COMM3.C
X *
X * Version 4.01A by Carlo Borreo & Cesare Dieni 17-Feb-90
X * Version 5.00L by Urban Mueller 17-Feb-91
X *
X */
X
X#include "shell.h"
X
X/* comm3.c */
Xstatic void doassign(char *log, char *phy);
Xstatic void assignlist(void);
Xstatic int strings_in_file(char *s);
Xstatic int htype_a_file(char *s);
Xstatic void install_menu(char **mav, int mac);
Xstatic int line_filter( char *(*line)(char *) );
X
Xstatic int
Xmyfgets( char *buf, FILE *in )
X{
X	int l;
X	char *ret;
X	if( ret=fgets(buf,253,in) ) {
X		l=strlen(buf);
X		if( buf[l-1]!='\n' )
X			buf[l]='\n', buf[l+1]=0;
X	}
X	return ret!=NULL && !dobreak();
X}
X
Xdo_tee( void )
X{
X	char buf[256];
X	FILE *out;
X
X	prepscroll( ac==1 );
X	if( ac>2 ) { ierror( av[2], 500 ); return 20; }
X	if( ac==1 ) out=stderr;
X	else if( !(out=fopen( av[1], "w" ))) { pError( av[1] ); return 20; }
X	while (myfgets(buf,stdin)) {
X		fputs(buf, stdout);
X		quickscroll();
X		fprintf(out, "%s", buf);
X	}
X	if( ac!=1 ) fclose( out );
X	return 0;
X}
X
Xdo_head( char *garbage, int com )
X{
X	int i, n;
X	FILE *f;
X	char buf[256];
X
X	if (ac>2) {
X		n=(int)(long)Atol(av[2]);
X		if (IoErr()) {
X			ierror(av[2],511);
X			return 20;
X		}
X	} else n=10;
X
X	f=fopen(av[1], "r");
X	if (f==NULL) {
X		pError(av[1]);
X		return 20;
X	}
X	if (com) {	/* tail specific part */
X		i=0;
X		while (fgets(buf, 256, f) && ! dobreak()) i++;
X		rewind(f);
X		if (n>i) n=i;
X		i=i-n;
X		while (i-- && fgets(buf, 256, f) && ! dobreak()) ;
X	}
X	for (i=1; i<=n && fgets(buf, 256, f) && ! dobreak(); i++)
X		printf("%s", buf);
X	fclose(f);
X	return 0;
X}
X
Xstatic int
Xexword( char **src, char *buf )
X{
X	*buf=0;
X	if( **src==0 ) return 0;
X	while( **src && **src!=0xA0 )
X		*buf++=*(*src)++;
X	*buf=0;
X	if( **src ) (*src)++;
X	return 1;
X}
X
Xstatic char helpfound=0;
X
Xvoid
Xman( FILE *f, char *s)
X{
X	char buf[140], entry[100];
X	int len=sprintf(entry, "    %s", s);
X
X	prepscroll(0);
X	rewind(f);
X	do                                  /* look for required argument */
X		if (fgets(buf, 140, f) == NULL)
X			return;
X	while ( Strncmp(entry, buf, len) );
X	helpfound=1;
X	do {                                /* display help */
X		quickscroll();
X		printf("%s", buf);
X		if (fgets(buf, 140, f) == NULL) return;
X	} while ( ( !isalphanum(*buf) ) && strncmp(buf, "    ", 4) && !dobreak() );
X}
X
X
Xdo_man( void )
X{
X	FILE *f;
X	int i;
X	char buf[200], name[60], *src, *var, docfound=0;
X
X	buf[0]=0;
X	if( var=get_var(LEVEL_SET,"_man" ) )
X		strcpy(buf,var);
X
X	if (ac==1) ac=2, av[1]="MAN";
X	for (i=1; i<ac; i++) {
X		src=buf, helpfound=0;
X		while( exword( &src, name) )
X			if( f=fopen(name, "r") ) {
X				docfound=1;
X				man(f, av[i]);
X				fclose(f);
X				if( helpfound )
X					break;
X			}
X		if( !docfound )
X			fprintf(stderr,"%s not found\n",buf);
X		else if( !helpfound )
X			fprintf(stderr, "Help not found for %s\n", av[i]);
X	}
X	return 0;
X}
X
Xdo_assign( void )
X{
X	int i;
X
X	if     (  ac==1  ) assignlist();
X	else if(  ac==2  ) doassign(av[1], NULL);
X	else if( !(ac&1) ) ierror(NULL, 500);
X	else
X		for( i=1; i<ac; i+=2 )
X			doassign( av[i],av[i+1] );
X	return 0;
X}
X
Xstatic char *assign_errors[4]={
X	"",
X	"Name %s is not valid\n",
X	"Weird error\n",
X	"Can't cancel %s:\n"
X	};
X
X
Xstatic void
Xdoassign(char *log, char *phy)
X{
X	int last=strlen(log) - 1;
X
X	if (log[last] != ':') fprintf(stderr, "Bad name %s\n", log);
X	else {
X		log[last] = 0;
X		if( options && phy && o_kick20 ) {
X			BPTR lock;
X			int succ=0;
X			if     ( options&1 ) succ=AssignLate( (UBYTE*)log,(UBYTE*)phy );
X			else if( options&2 ) succ=AssignPath( (UBYTE*)log,(UBYTE*)phy );
X			else if( options&4 )
X				if( lock=Lock(phy,ACCESS_READ) )
X					if( !(succ=AssignAdd(log,lock)))
X						UnLock(lock);
X			if( !succ )
X				pError( log );
X		} else if( !(options&4) ) {
X			int err=Assign(log, phy);
X			fprintf(stderr,assign_errors[err],err==1?phy:log);
X		}
X	}
X}
X
Xextern struct RootNode2x *RootNode;
X#define ENTRIES 128
X
Xstatic void
Xassignlist()
X{
X	char *ptr, *log, **arr;
X	int ctr, i;
X
X	if(!(arr=expand_devs()))
X		return;
X
X	printf("Devices:\n");
X	for (i=ctr=0; arr[i] && *arr[i]==1; i++) {
X		printf("%-8s",arr[i]+1);
X		if (ctr++ == 5) { ctr=0; printf("\n"); }
X	}
X
X	printf("\n\nVolumes:\n");
X	for( ; arr[i] && *arr[i]==2; i++ )
X		printf( "%-16s [Mounted]\n",arr[i]+1);
X
X	printf("\nDirectories:\n");
X	for( ; arr[i] && !dobreak(); i++ ) {
X		log=arr[i]+1; ptr=log+strlen(log)+1;
X		switch( *(log-1)) {
X		case 3:
X			printf("%-20s%s\n", log, ptr);
X			for(;;) {
X				ptr+=strlen(ptr)+1;
X				if( !*ptr ) break;
X					printf("%-19s+%s\n", "", ptr);
X			}
X			break;
X		case 4: printf("%-20s<%s>\n", log, ptr); break;
X		case 5: printf("%-20s[%s]\n", log, ptr); break;
X		}
X	}
X
X	free_expand( arr );
X}
X
Xchar **
Xexpand_devs(void)
X{
X	static char Type[]={1,3,2,4,5};
X	struct DosInfo *info=(void*)((long)RootNode->rn_Info*4);
X	struct DosList2x *list=(void*)((long) info->di_DevInfo*4);
X	struct AssignList2x *path;
X	char buf[256], **arr, *ptr;
X	int n;
X
X	if( !(arr=malloc(ENTRIES*sizeof(char *))))
X		return NULL;
X
X	Forbid();
X	for( n=0; list && n<ENTRIES; list=(void*)((long)list->dol_Next*4)) {
X		if( list->dol_Type<0 || list->dol_Type>DLST_NONBINDING )
X			continue;
X		ptr=buf;
X		*ptr++=Type[list->dol_Type];
X		ptr+=BtoCStr(ptr,list->dol_Name,200);
X		*ptr++=':'; *ptr++=0;
X		switch( list->dol_Type) {
X		case DLST_DIRECTORY : 
X			if(list->dol_Lock)
X				PathName(list->dol_Lock, ptr, 200);
X			else 
X				strcpy(ptr,"Nonexisting lock");
X			ptr+=strlen(ptr)+1;                    
X			path=(void*)list->dol_misc.dol_assign.dol_List;
X			for( ; path; path=(void*)path->al_Next ) {
X				if(list->dol_Lock)
X					PathName(path->al_Lock, ptr, 200);
X				else 
X					strcpy(ptr,"Nonexisting lock");
X				ptr+=strlen(ptr)+1;
X			}
X			*ptr++=0;
X			break;
X		case DLST_LATE      : 
X		case DLST_NONBINDING:
X			ptr+= sprintf(ptr,"%s",list->dol_misc.dol_assign.dol_AssignName);
X			*ptr++=0;
X			break;
X		}
X		arr[n]=salloc(ptr+1-buf);
X		memcpy(arr[n++],buf,ptr+1-buf);
X	}
X	arr[n]=NULL;
X	Permit();
X
X	QuickSort( arr, n );
X
X	return arr;
X}
X
X
Xdo_join( void )
X{
X	BPTR sou, dest;
X	char *buffer;
X	int i;
X	long n;
X	char *namedest=av[--ac];
X
X	if (options==0 && exists(namedest)) { ierror(namedest,203); return 20; }
X	if ( (buffer=malloc(8192)) == NULL ) { ierror(NULL,103); return 20; }
X	if ( (dest=Open(namedest, MODE_NEWFILE)) == NULL )
X		{ pError(namedest); goto fail1; }
X	for (i=1; i<ac; i++) {
X		if ( (sou=Open(av[i], MODE_OLDFILE)) == NULL ) pError(av[i]);
X		else
X			while( (n=Read(sou, buffer, 8192L)) > 0 )
X				if (Write(dest, buffer, n) != n)
X					{ pError(namedest); Close(sou); goto fail2; }
X		Close(sou);
X	}
Xfail2:
X	Close(dest);
Xfail1:
X	free(buffer);
X	return 0;
X}
X
X#define BUFDIM 512L
X#define MAXSTR 256
X
Xint minstr;
X
Xstatic int
Xstrings_in_file(char *s)
X{
X	char c;
X	char readbuf[BUFDIM+1], strbuf[MAXSTR+1];
X	int i, strctr=0;
X	BPTR fh;
X	int out, n;
X
X	prepscroll(0);
X	if ( fh=Open(s, MODE_OLDFILE) ) {
X		fprintf(stderr, "Strings in %s (len>=%d):\n",s,minstr);
X		while ( (n=(int)Read(fh, readbuf, BUFDIM)) > 0 && !CHECKBREAK() )
X			for (i=0; i<n; i++) {
X				c=readbuf[i];
X				if (c<0x20 || c>0x7f) {
X					out=(strctr>=minstr);
X					if (!out) strctr=0;
X				} else {
X					strbuf[strctr++]=c;
X					out=(strctr>=BUFDIM);
X				}
X				if (out) {
X					strbuf[strctr]='\0';
X					puts(strbuf);
X					quickscroll();
X					strctr=0;
X				}
X			}
X		Close(fh);
X	} else
X		pError(s);
X	return 0;
X}
X
Xdo_strings( void )
X{
X	minstr=myatoi(av[--ac],1,255);
X	all_args( strings_in_file, 0);
X	return 0;
X}
X
XBPTR myfile[MAXMYFILES];
X
Xdo_open( void )
X{
X	long mode;
X	int n;
X
X	switch (toupper(av[2][0])) {
X		case 'R': mode=MODE_OLDFILE; break;
X		case 'W': mode=MODE_NEWFILE; break;
X		default : ierror(NULL,500); return 1;
X	}
X	n=myatoi(av[3],0,MAXMYFILES-1); if (atoierr) return 20;
X	if (myfile[n]) myclose(n);
X	myfile[n]=Open(av[1],mode);
X	return myfile[n]==NULL;
X}
X
Xdo_close( void )
X{
X	int i, n;
X
X	if (ac==1)
X		for (i=1; i<MAXMYFILES; i++)
X			myclose(i);
X	for (i=1; i<ac; i++) {
X		n=myatoi(av[i],0,MAXMYFILES-1); if (atoierr) return 20;
X		myclose(n);
X	}
X	return 0;
X}
X
Xvoid
Xmyclose(int n)
X{
X	if (myfile[n]) { Close(myfile[n]); myfile[n]=NULL; }
X}
X
Xdo_fileslist( void )
X{
X	int i, flag=0;
X
X	printf("Open files:");
X	for (i=0; i<MAXMYFILES; i++)
X		if (myfile[i]) { printf(" %d",i); flag=1; }
X	if (!flag) printf(" None!");
X	printf("\n");
X	return 0;
X}
X
XBPTR
XextOpen( char *name, long mode )
X{
X	if (name[0]=='.') return myfile[atoi(name+1)];
X	return Open(name,mode);
X}
X
X
Xvoid
XextClose(BPTR fh)
X{
X	int i;
X
X	for (i=0; i<MAXMYFILES; i++)
X		if (myfile[i]==fh) return;
X	Close(fh);
X}
X
Xdo_basename( void )
X{
X	set_var(LEVEL_SET, av[1], BaseName(av[2]));
X	return 0;
X}
X
Xdo_tackon( void )
X{
X	char buf[256];
X
X	strcpy(buf, av[2]);
X	TackOn(buf, av[3]);
X	set_var(LEVEL_SET, av[1], buf);
X	return 0;
X}
X
Xextern char shellres[];
X
Xdo_resident( void )
X{
X	int i=1;
X	struct ResidentProgramNode *p;
X	char buf[256];
X
X	if (options==0 && ac>1) options=1;
X	switch (options) {
X	case 0:
X		ObtainSemaphore (& (ArpBase->ResPrgProtection) );
X		if (p=ArpBase->ResidentPrgList) {
X			printf("Name             Users Access\n");
X			for (; p; p=p->rpn_Next)
X				printf("%-17s%5d%6d\n",
X					p->rpn_Name, p->rpn_Usage, p->rpn_AccessCnt);
X		} else
X			printf("No resident program(s)\n");
X		ReleaseSemaphore(& (ArpBase->ResPrgProtection) );
X		break;
X	case 1:
X		for (; i<ac; i++)
X			if (loadres(av[i]))
X				printf("OK! %s is now resident\n", BaseName(av[i]));
X			else
X				pError(av[i]);
X		break;
X	case 2:
X		for (; i<ac; i++)
X			if (RemResidentPrg(av[i])) ierror(av[i],202);
X			else printf("Removed %s\n",av[i]);
X		break;
X	case 4:
X		for (; i<ac; i++) {
X			if( !o_resident ) {
X				Setenv(shellres,"1");
X				o_resident=1;
X			}
X			sprintf(buf,"res_%s",av[i]);
X			Setenv(buf,"1");
X		}
X		break;
X	default:
X		ierror(NULL,500);
X		break;
X	}
X	return 0;
X}
X
Xint
Xloadres(char *s)
X{
X	BPTR seg;
X
X	if (seg=(BPTR)LoadPrg(s)) AddResidentPrg(seg,BaseName(s));
X	return (seg != NULL);
X}
X
Xstatic struct ProcessControlBlock pcb={
X	4000,		/* pcb_StackSize	*/
X	0,			/* pcb_Pri			*/
X	};
X/* remaining fields are NULL */
X
Xdo_truerun(char *avline, int backflag)
X{
X	char name[100], *args, buf[10];
X	int cli;
X
X	if (backflag) {
X		pcb.pcb_Control=NULL;
X		pcb.pcb_Input=pcb.pcb_Output=Open("NIL:",MODE_OLDFILE);
X	} else {
X		pcb.pcb_Control=NULL;
X		pcb.pcb_Input=pcb.pcb_Output =NULL;
X	}
X	args=next_word(next_word(avline));
X
X	if((cli=ASyncRun(av[1],args,&pcb))<0)
X		if (dofind(av[1], "", name,v_path))
X			cli=ASyncRun(name,args,&pcb);
X
X	sprintf(buf,"%d",cli);
X	set_var(LEVEL_SET,"_newproc",buf);
X
X	if( cli<0 ) {
X		ierror(av[1],205);
X		return 20;
X	}
X	return 0;
X}
X
Xint
Xexists( char *name )
X{
X	BPTR lock;
X
X	if (lock=Lock(name,ACCESS_READ)) {
X		UnLock(lock);
X		return 1;
X	}
X	return 0;
X}
X
Xdo_aset( void )
X{
X	Setenv(av[1],av[2]);
X	return 0;
X}
X
X#define HTYPELINE 16L
X
Xstatic int
Xhtype_a_file( char *s )
X{
X	BPTR fh;
X	long n, filesize=0;
X	char buf[HTYPELINE+1];
X	char out[80], *put;
X	int i;
X
X	if ( (fh=Open(s,MODE_OLDFILE))==NULL ) { pError(s); return 20; }
X	prepscroll(0);
X	while ( (n=Read(fh,buf,HTYPELINE))>0 && !dobreak()) {
X		put=out;
X		put+=sprintf(put,"%06lx: ",filesize);
X		filesize+=n;
X		for (i=0; i<n; i++) {
X			put+=sprintf( put,(i&3) ? "%02x" : " %02x",buf[i]);
X			if ((buf[i]&127)<0x20) buf[i]='.';
X		}
X		for ( ; i<HTYPELINE; i++) {
X			put+=sprintf( put, (i&3) ? "  " : "   ");
X			buf[i]=' ';
X		}
X		buf[i]=0;
X		sprintf(put,"    %s",buf);
X		puts(out);
X		quickscroll();
X	}
X	Close(fh);
X	return 0;
X}
X
Xdo_htype( void )
X{
X	all_args( htype_a_file, 0);
X	return 0;
X}
X
Xdo_stack( void )
X{
X	long n;
X
X	if (ac>1) {
X		n=Atol(av[1]);
X		if (!IoErr()) Mycli->cli_DefaultStack=(long)(n >> 2L);
X	}
X	else printf("current stack size is %ld bytes\n",
X				(long)Mycli->cli_DefaultStack << 2L);
X	return 0;
X}
X
Xdo_fault( void )
X{
X	struct PERROR *p;
X	int i, n;
X
X	for (i=1; i<ac; i++) {
X		n=myatoi(av[i],0,32767);
X		if (!atoierr) {
X			for (p=Perror; p->errnum && p->errnum!=n; p++);
X			if (p->errnum)
X				printf("Fault %d: %s\n",n,p->errstr);
X			else
X				printf("Fault %d not recognized\n",n);
X		}
X	}
X	return 0;
X}
X
Xstruct rpncommand {
X	char *str;
X	int parsin, parsout;
X	};
X
Xstatic struct rpncommand rpn[]={
X	"+",	2,	1,
X	"-",	2,	1,
X	"*",	2,	1,
X	"/",	2,	1,
X	"%",	2,	1,
X	"&",	2,	1,
X	"|",	2,	1,
X	"~",	1,	1,
X	">",	2,	1,
X	"<",	2,	1,
X	"==",	2,	1,
X	"!",	1,	1,
X	"MAX",	2,	1,
X	"MIN",	2,	1,
X	"DUP",	1,	2,
X	"DROP",	1,	0,
X	"SWAP",	2,	2,
X	"HELP",	0,	0,
X	NULL,	0,	1,	/* this looks for a number */
X};
X
Xstatic long stack[50];
Xstatic int sp;
X
X
Xeval_rpn( char **av, int ac, int flag )
X{
X	char *zero="Division by zero\n";
X	struct rpncommand *temp;
X	long n0, n1, t;
X	int j, i=0, oldsp=sp;
X
X	for (; i<ac; i++) {
X		for (j=0; rpn[j].str && Strcmp(rpn[j].str,av[i]); j++) ;
X		n0=stack[sp-1];
X		n1=stack[sp-2];
X		sp -= (rpn[j].parsin);
X		if (sp<0) { fprintf(stderr, "RPN: Empty stack\n"); goto error; }
X		switch (j) {
X		  case 0:	n0 += n1;			break;
X		  case 1:	n0 = n1-n0;			break;
X		  case 2:	n0 *= n1;			break;
X		  case 3:	if(n0) n0=n1/n0; else fprintf(stderr,zero); break;
X		  case 4:	if(n0) n0=n1%n0; else fprintf(stderr,zero); break;
X		  case 5:	n0 &= n1;			break;
X		  case 6:	n0 |= n1;			break;
X		  case 7:	n0 =  ~n0	;		break;
X		  case 8:	n0 = (n1 > n0);		break;
X		  case 9:	n0 = (n1 < n0);		break;
X		  case 10:	n0 = (n0 == n1);	break;
X		  case 11:	n0 = !n0;			break;
X		  case 12:	n0=n1>n0 ? n1 : n0;	break;
X		  case 13:	n0=n1<n0 ? n1 : n0;	break;
X		  case 14:	n1=n0;				break;
X		  case 15:	t=n0; n0=n1; n1=t;	break;
X		  case 16:						break;
X		  case 17:	printf("In Commands Out\n");
X			for (temp=rpn; temp->str; temp++)
X			    printf(" %d %-10s%d\n",
X				temp->parsin,temp->str,temp->parsout);
X			break;
X		  default:	n0=Atol(av[i]);
X				if (IoErr()) {
X					fprintf(stderr, "Bad RPN cmd: %s\n",av[i]);
X					goto error;
X				}
X				break;
X		  }
X		stack[sp]=n0;
X		stack[sp+1]=n1;
X		sp += rpn[j].parsout;
X	}
X	if( flag && sp-1)
X		fprintf( 
X		  stderr,
X		  "RPN: Stack not empty\n"
X		);
X
Xexit:
X	t=sp; sp=oldsp;
X	if( flag )
X		return stack[t-1]; /* return top value */
X	else 
X		return t-sp;
X
Xerror:
X	sp=oldsp;
X	return 0;
X}
X
X
Xdo_rpn(char *garbage,int ifflag) /* ifflag!=0 if called from if */
X{
X	int i=1;
X	long t;
X
X	t=eval_rpn( av+i, ac-i, ifflag );
X	if (ifflag) return t;              /* called from if: return top value */
X	for (i=sp+t-1;i>=sp;i--) printf("%ld\n", stack[i]);/* else print stack */
X
X	return t ? 0 : 20;
X}
X
Xdo_path( void )
X{
X	ULONG ll, ll1, *lp, new, *newp;
X	char buf[256];
X	char buf2[256];
X	BPTR lock;
X	int i;
X
X	if( options&1 ) {
X		Forbid();
X		for( ll= Mycli->cli_CommandDir; ll; ll= ll1 ) {
X			lp=(ULONG *)(4*ll);
X			ll1=*lp;
X			DosFreeMem( lp );
X		}
X		Mycli->cli_CommandDir=0;
X		Permit();
X	} else if( ac==1 ) {     /* Should Forbid() here, but puts() Permit()s */
X		puts("Current dir"); /* and failure here is not really harmful...  */
X		for( ll= Mycli->cli_CommandDir; ll; ll= *lp ) {
X			lp=(ULONG *)(4*ll);
X			PathName(lp[1], buf, 256L);
X			puts(buf);
X		}
X		puts("C:");
X		return 0;
X	}
X	Forbid();
X	for( i=1; i<ac; i++ ) {
X		if( !(lock=Lock(av[i],ACCESS_READ)) ) {
X			pError(av[i]);
X			continue;
X		}
X		PathName(lock, buf, 256L);
X		for( ll= Mycli->cli_CommandDir, lp=NULL; ll; ll= *lp ) {
X			lp=(ULONG *)(4*ll);
X			PathName(lp[1],buf2,256L);
X			if( !Strcmp(buf,buf2) ) {
X				UnLock(lock);
X				break;
X			}
X		}
X		if( !ll && (newp=DosAllocMem( 8 ))) {
X			newp[1]=lock;
X			new =(ULONG)newp/4;
X			if( lp )
X				*lp=new;
X			else
X				Mycli->cli_CommandDir=new;
X		}
X	}
X	Permit();
X	return 0;
X}
X
Xdo_pri( void )
X{
X	int t, pri;
X	struct Process *proc;
X
X	t=(int)(long)FindCLI(0L);
X	t=myatoi(av[1],0,t); if (atoierr) return 20;
X	pri=myatoi(av[2],-128,127); if (atoierr) return 20;
X	Forbid();
X	proc=(t==0 ? Myprocess : FindCLI((long)t));
X	if (proc==NULL) fprintf(stderr, "process not found\n");
X	else SetTaskPri((struct Task *)proc, (long)pri);
X	Permit();
X	return 0;
X}
X
Xdo_strleft( void )
X{
X	int n;
X
X	n=posatoi(av[3]); if (atoierr) return 20;
X	set_var_n(LEVEL_SET, av[1], av[2], n);
X	return 0;
X}
X
Xdo_strright( void )
X{
X	int n, len=strlen(av[2]);
X
X	n=posatoi(av[3]); if (atoierr) return 20;
X	if( n>len ) n=len;
X	set_var(LEVEL_SET, av[1], av[2]+len-n );
X	return 0;
X}
X
Xdo_strmid( void )
X{
X	int n1, n2=999999, len=strlen(av[2]);
X
X	n1=myatoi(av[3],1,999999)-1; if (atoierr) return 20;
X	if (n1>len) n1=len;
X	if (ac>4) {
X		n2=posatoi(av[4]); if (atoierr) return 20;
X	}
X	set_var_n(LEVEL_SET, av[1], av[2]+n1, n2);
X	return 0;
X}
X
Xdo_strlen( void )
X{
X	char buf[16];
X
X	sprintf(buf,"%d",strlen(av[2]));
X	set_var(LEVEL_SET, av[1], buf);
X	return 0;
X}
X
Xint atoierr;
X
Xmyatoi(char *s,int mmin,int mmax)
X{
X	int n;
X
X	n=Atol(s);
X	if (atoierr=IoErr())
X		ierror(s,511);
X	else if (n<mmin || n>mmax) {
X		atoierr=1; n=mmin;
X		fprintf( stderr, "%s(%d) not in (%d,%d)\n",s,n,mmin,mmax );
X	}
X	return n;
X}
X
Xunlatoi(char *s)
X{
X	int n=Atol(s);
X	if (atoierr=IoErr())
X		ierror(s,511), n=0;
X	return n;
X}
X
Xposatoi(char *s)
X{
X	int n=Atol(s);
X	if (atoierr=IoErr())
X		ierror(s,511);
X	else if (n<0 )
X		atoierr=1, n=0, fprintf( stderr, "%s must be positive\n",s );
X	return n;
X}
X
X
Xdo_fltlower( void )
X{
X	return line_filter( strlwr );
X}
X
Xdo_fltupper( void )
X{
X	return line_filter( strupr );
X}
X
X#if 0
Xchar *
Xstripcr( char *get )
X{
X	char *old=get, *put;
X
X	for( put=get; *get; get++ )
X		if( *get!=13 )
X			*put++=*get;
X	*put++=0;
X	return old;
X}
X
Xdo_fltstripcr( void )
X{
X	return line_filter( stripcr );
X}
X#endif
X
Xint
Xline_filter( char *(*func)( char * ) )
X{
X	char buf[256];
X
X	while (!CHECKBREAK() && gets(buf))
X		puts((*func)(buf));
X	return 0;
X}
X
Xdo_linecnt( void )
X{
X	int count=0;
X	char buf[256];
X
X	while (!CHECKBREAK() && gets(buf)) ++count;
X	printf("%d lines\n",count);
X	return 0;
X}
X
Xdo_uniq( void )
X{
X	int firstline=1;
X	char buf[256], oldbuf[256];
X
X	while (!CHECKBREAK() && gets(buf)) {
X		if ( firstline || strcmp(buf, oldbuf)) {
X			strcpy(oldbuf, buf);
X			puts(buf);
X		}
X		firstline=0;
X	}
X	return 0;
X}
X
X
X#define RXFB_RESULT  17
X
Xstatic struct rexxmsg {
X	struct Message cm_Node;
X	LONG   RFU1;
X	LONG   RFU2;
X	LONG   rm_Action;
X	LONG   rm_Result1;
X	LONG   rm_Result2;
X	char   *cm_Args[16];
X	LONG   RFU7;
X	LONG   RFU8;
X	LONG   RFU9;
X	LONG   RFU10;
X	LONG   RFU11;
X	LONG   RFU12;
X} mymsg;
X
Xdo_rxsend( char *avline )
X{
X	int i;
X	long result;
X	struct MsgPort *port, *reply;
X	long len;
X	char buf[20], *resptr;
X
X	if (!(port = FindPort(av[1])))
X		{ fprintf(stderr, "No port %s!\n", av[1]); return 20; }
X	mymsg.cm_Node.mn_Node.ln_Type = NT_MESSAGE;
X	mymsg.cm_Node.mn_Length = sizeof(struct rexxmsg);
X	mymsg.rm_Action = (options&1 ? 1L << RXFB_RESULT : 0);
X	if (!(reply = CreatePort(NULL, 0L))) {
X		fprintf(stderr, "No reply port\n");
X		return 20;
X	}
X	mymsg.cm_Node.mn_ReplyPort = reply;
X
X	if( options&2 )
X		av[2]=compile_av( av,2,ac,' ',0), ac=3;
X	for ( i=2; i<ac; i++) {
X		mymsg.cm_Args[0] = av[i];
X		mymsg.rm_Result2 = 0;        /* clear out the last result. */
X		PutMsg(port, &mymsg.cm_Node);
X		WaitPort(reply);
X
X		if (options&1) {
X			if( (result=mymsg.rm_Result2)<1000000 ) { /* so does it AREXX */
X				sprintf(buf,"%d",result);              
X				set_var(LEVEL_SET,v_result,buf);
X			} else {
X				resptr=(char *)(result-4);
X				len=*(long *)resptr;
X				memmove(resptr,resptr+4,len);  /* Null terminate */
X				resptr[len]=0;      
X				set_var(LEVEL_SET,v_result,resptr);
X				FreeMem(resptr, len+4 );
X			}
X		} else 
X			unset_var( LEVEL_SET, v_result );
X	}
X	if( options&2 )
X		free( av[2] );
X
X	if (reply) DeletePort(reply);
X	return 0;
X}
X
Xstatic char *rxreturn;
X
Xdo_rxrec( void )
X{
X	struct MsgPort *port;
X	struct rexxmsg *msg;
X	char *portname, *str;
X
X	if (ac > 1)
X		portname=av[1];
X	else
X		portname="rexx_csh";
X
X	port=CreatePort(portname, 0L);
X	if (port==NULL) {
X		fprintf(stderr, "Can't have MsgPort %s\n", portname);
X		return 20;
X	}
X	for (;;) {
X		WaitPort(port);
X		while (msg=(struct rexxmsg *)GetMsg(port)) {
X			if ( ! Strcmp(msg->cm_Args[0], "bye")) {
X				ReplyMsg((struct Message *)msg);
X				DeletePort(port);
X				return 0;
X			}
X			rxreturn=NULL;
X			exec_command(msg->cm_Args[0]);
X			if (msg->rm_Action & (1L << RXFB_RESULT)) {
X				if( rxreturn ) {
X					str= SAllocMem( strlen( rxreturn )+5 , 0 );
X					*(long *)str=strlen( rxreturn );
X					strcpy( str+4, rxreturn );
X					msg->rm_Result2=(long)str;
X				} else {
X					str = get_var(LEVEL_SET, v_lasterr);
X					msg->rm_Result2=(str) ? atoi(str) : 20;
X				}
X			}
X			ReplyMsg((struct Message *)msg);
X		}
X	}
X}
X
Xint
Xdo_waitport( void )
X{
X	int count=4*10;
X	struct MsgPort *port=NULL;
X
X	if( ac==3 ) 
X		{ count=2*myatoi(av[2],0, 32000); if( atoierr ) return 20; }
X
X	while( --count>=0 && !(port=FindPort(av[1])) && !dobreak() )
X		Delay(12);
X
X	return port ? 0 : 20;
X}
X
Xint
Xdo_rxreturn( void )
X{
X	rxreturn=compile_av( av, 1, ac, ' ', 1 );
X	return 0;
X}
X
Xdo_ascii( void )
X{
X	int x=1, y, c, c1, t;
X	char *fmt1=" %3d %c%c |", *fmt2=" %4d";
X
X	if( options&1 ) fmt1=" %3o %c%c |", fmt2="%4o";
X	if( options&2 ) fmt1=" %3x %c%c |", fmt2="%4x";
X	if( ac==x )
X		for( y=0; y<32 && !dobreak(); y++ ) {
X			printf("|");
X			for( x=0; x<8; x++ ) {
X				c1=c=y+32*x; t=' ';
X				if( c<32 ) t='^', c1+=64;
X				printf(fmt1,c, t, c1<128 || c1>=160?c1:'.');
X			}
X			printf("\n");
X		}
X	else 
X		for( ; x<ac && !dobreak(); x++ ) {
X			for( y=0; y<strlen(av[x]); y++ )
X				printf(fmt2,av[x][y]);
X			printf("\n");
X		}
X	return 0;
X}
X
Xvoid
Xappendslash( char *path )
X{
X	int c;
X
X	if( (c=path[strlen(path)-1]) !='/' && c!=':' )
X		strcat(path,"/");
X}
X
Xstatic void
Xwhereis( char *path, char *file )
X{
X	char **eav, buf[100];
X	int  eac, j;
X
X	buf[0]=0;
X	if( path ) {
X		strcpy(buf,path);
X		appendslash(buf);
X	}
X	strcat(buf,".../");
X	strcat(buf,file);
X	if( !index( file, '*' ) && !index( file, '?') )
X		strcat(buf,"*");
X	if(eav=expand(buf,&eac)) {
X		for( j=0; j<eac && !dobreak(); j++ )
X			printf("%s\n",eav[j]);
X		free_expand(eav);
X	}
X}
X
Xdo_whereis( void )
X{
X	char buf[200], *prev, *devs;
X	int i;
X
X	if( index( av[1],':') || index( av[1],'/' ) )
X		{ fprintf(stderr,"No paths please\n"); return 20; };
X
X	if( options&1 ) {
X		Myprocess->pr_WindowPtr = (APTR)(-1);
X		get_drives( devs=buf );
X		do {
X			prev=devs; devs=index(devs,0xA0);
X			if( devs ) *devs++=0; 
X			whereis( prev, av[1] );
X		} while( devs );
X		Myprocess->pr_WindowPtr = (APTR) o_noreq;
X	} else if( ac==2 ) {
X		whereis( NULL, av[1] );
X	} else {
X		for( i=2; i<ac; i++ ) {
X			strcpy(buf,av[i]);
X			appendslash( buf );
X			whereis( buf, av[1] );
X		}
X	}
X	return 0;
X}
X
Xdo_usage( void )
X{
X	int i;
X
X	if( ac==1 ) {
X		printf("Usage: usage [command...command]\n");
X		printf("[ ]=option   [ | ]=choice   { }=repetition   name...name=1 or more names\n");
X	} else 
X		for( i=1; i<ac; i++ )
X			show_usage( av[i] );
X	return 0;
X}
X
Xint NumMenus;
X
Xdo_menu( void )
X{
X	if( o_nowindow )
X		return 5;
X
X	if( options&1 )
X		remove_menu();
X
X	if( ac==2 )
X		show_usage( NULL );
X	else if( NumMenus<MAXMENUS && ac!=1)
X		install_menu( av+1, ac-1 );
X
X	set_menu();
X	return 0;
X}
X
X#define NUMITE 40
X#define TITWID 90
X#define ITEWID 148
X
Xstatic struct Menu DefaultMenu= {0, 0,0,TITWID,10, MENUENABLED,0,0};
Xstatic struct IntuiText DefaultIntuiText= {0,1,JAM2, 1,1,NULL,0,0};
Xstatic struct MenuItem DefaultMenuItem=
X  {0, 0,0,ITEWID,0, HIGHCOMP|ITEMTEXT|ITEMENABLED,0,0,0,0,0,0};
X
Xstruct Menu Menus[10];
Xchar *MenuCommand[MAXMENUS][MAXITEMS];
X
Xextern char *rindex();
X
Xstatic void
Xinstall_menu( char *mav[], int mac )
X{
X	struct TextAttr *ta;
X	struct Menu *m;
X	struct MenuItem *mi, **pmi;
X	struct IntuiText *it;
X	int y, i, fonthei;
X	char *p, *com;
X
X	if( o_nowindow || !Win )
X		return;
X
X	if( mac>=MAXITEMS )
X		mac=MAXITEMS-1;
X
X	ClearMenuStrip( Win );
X	Delay(3);
X
X	if( NumMenus )
X		Menus[NumMenus-1].NextMenu=Menus+NumMenus;
X	m  =&Menus[NumMenus];
X	*m =DefaultMenu;
X	m->LeftEdge  = NumMenus*TITWID;
X	m->MenuName  = strcpy(salloc(strlen(mav[0])+1),mav[0]);
X	if( strlen(m->MenuName)>TITWID/8 )
X		m->MenuName[TITWID/8+1]=0;
X	DefaultIntuiText.ITextFont=ta=Win->WScreen->Font;
X	DefaultMenuItem.Height=2+(fonthei=ta->ta_YSize);
X
X	y=0;
X	pmi=&m->FirstItem;
X	for( i=1; i<mac; i++) {
X		it =(void *)salloc(sizeof(struct IntuiText));
X		*it=DefaultIntuiText;
X		mi =(void *)salloc(sizeof(struct MenuItem ));
X		*mi=DefaultMenuItem;
X
X		com=NULL;
X		if( p=index(mav[i],',')) {
X			*p=0; com=++p;
X			if( p=index(com,',')) {
X				*p=0;
X				mi->Command=p[1];
X				mi->Flags |=COMMSEQ;
X			}
X		}
X
X		if( !com || !*com) {
X			com=strcpy(salloc(strlen(mav[i])+2),mav[i]);
X			MenuCommand[NumMenus][i-1]=com;
X			com+=strlen(com);
X			*com++=13;
X			*com=0;
X		} else {
X			MenuCommand[NumMenus][i-1]=strcpy(salloc(strlen(com)+1),com);
X		}
X
X		it->IText=(UBYTE *)strcpy(salloc(strlen(mav[i])+2),mav[i]);
X
X		*pmi= mi;
X		pmi = &mi->NextItem;
X		mi->TopEdge = y;
X		mi->ItemFill= (APTR)it;
X
X		y+=DefaultMenuItem.Height;
X	}
X
X	NumMenus++;
XMError:
X	return;
X}
X
X
Xvoid
Xremove_menu()
X{
X	if( NumMenus>0 ) {
X		struct MenuItem *mi, *nextmi;
X		int i,j;
X
X		for( i=0; i<NumMenus; i++ ) {
X			for( mi=Menus[i].FirstItem,j=0 ; mi; mi=nextmi,j++ ) {
X				free( ((struct IntuiText *)mi->ItemFill)->IText );
X				free( ((struct IntuiText *)mi->ItemFill) );
X				nextmi=mi->NextItem;
X				free(mi);
X				free(MenuCommand[i][j]);
X			}
X		}
X
X		NumMenus=0;
X		set_menu();
X	}
X}
X
X
Xvoid
Xset_menu()
X{
X	if( o_nowindow || !Win )
X		return;
X
X	if( NumMenus>0 )
X		SetMenuStrip( Win, Menus );
X	else 
X		ClearMenuStrip( Win );
X
X	Delay(3);
X}
X
Xdo_getenv( void )
X{
X	char buf[256], *val=buf, *getenv();
X
X	if( ac!=3 && ac!=2 ) {
X		show_usage( NULL );
X		return 20;
X	}
X	if( !Getenv(av[ac-1],buf,256))
X		val="";
X
X	if( ac==2 )
X		printf( "%s\n", val );
X	else 
X		set_var( LEVEL_SET, av[1], val );
X	return 0;
X}
X
X
Xdo_setenv( void )
X{
X	if( ac!=3 ) {
X		show_usage( NULL );
X		return 20;
X	} else
X		setenv( av[1], av[2] );
X	return 0;
X}
X
Xchar **
Xread_name( char *name, int *ac )
X{
X	FILE *file;
X	char **av=NULL;
X
X	*ac=0;
X	if( file=name ? fopen( name, "r") : stdin ) {
X		av=read_file( file, ac );
X		if( name ) fclose( file );
X	} else 
X		pError( name );
X	return av;
X}
X
X
Xchar **
Xread_file( FILE *file, int *ac )
X{
X	int buflen=4096, lines=0, i, offs;
X	char *buf, *ptr, *got=NULL, **lineptr;
X
X	if( !(buf=ptr=malloc( buflen )))
X		goto error;
X	do {
X		while( ptr+400 < buf+buflen && (got=fgets( ptr, 400, file ) ) &&
X		       !dobreak()) {
X			ptr+=strlen(ptr)-1, lines++;
X			*ptr++=0;
X		}
X		if( ptr+256 < buf+buflen ) {
X			offs=ptr-buf;
X			if( !(buf=realloc( buf, buflen*=2 ) ) )
X				goto error;
X			ptr=buf+offs;
X		}
X	} while( got && !dobreak());
X	if( !(lineptr=(char **)malloc( (lines+1)*sizeof( char * ))))
X		goto error;
X	*lineptr++=buf;
X	for( ptr=buf, i=0; i<lines; i++ ) {
X		lineptr[i]=ptr;
X		ptr+=strlen(ptr)+1;
X	}
X	*ac=lines;
X	return lineptr;
X
Xerror:
X	if( buf ) free( buf );
X	fprintf( stderr, "Out of memory\n" );
X	*ac=0;
X	return NULL;
X}
X
Xvoid
Xfree_file( ptr )
X	char **ptr;
X{
X	if( ptr-- ) {
X		if( *ptr )
X			free( *ptr );
X		free(ptr);
X	}
X}
X
X
Xdo_qsort( void )
X{
X	char **lineptr;
X	int  lines, i;
X
X	if( ac==1 ) {
X		lineptr=read_file( stdin, &lines);
X		DirQuickSort( lineptr, lines, cmp, options&1, 0 );
X		prepscroll(0);
X		for( i=0; i<lines && !dobreak(); i++ ) {
X			quickscroll();
X			puts( lineptr[i] );
X		}
X		free_file( lineptr );
X	}
X	return 0;
X}
X
Xextern int w_width;
X
Xdo_truncate( void )
X{
X	char buf[256];
X	int  w=newwidth(), c;
X	char *ptr;
X
X	if( ac==2 )
X		w=atoi( av[1] );
X
X	prepscroll(0);
X	while( gets(buf) && !dobreak() ) {
X		for( c=0, ptr=buf; *ptr && c<w; ptr++ )
X			if( *ptr=='\t' )
X				c+=8-(c&7);
X			else if( *ptr==27 ) {
X				while( *ptr<'@' )
X					ptr++;
X			} else 
X				c++;
X		*ptr=0;
X		quickscroll();
X		puts(buf);
X	}
X	return 0;
X}
X
Xint
Xdo_readfile( void )
X{
X	char **rav, *str=NULL;
X	int rac;
X
X	if( rav= read_name( ac>2 ? av[2] : NULL, &rac ) ) {
X		if( rac>255 ) rac=255;
X		if( str= compile_av( rav, 0, rac, 0xA0, 0 ) )
X			set_var( LEVEL_SET, av[1], str );
X		free_file( rav );
X	}
X	return str ? 0 : 20;
X}
X
Xint
Xdo_split( void )
X{
X	int i;
X	char *val, *gap, *oldval;
X
X	if( !(val=get_var( LEVEL_SET, av[1] )))
X		{ fprintf( stderr, "Variable %s undefined\n", av[0] ); return 20; }
X	oldval=val=strcpy(salloc(strlen(val)+1),val);
X	for( i=2; i<ac-1; i++ ) {
X		if( gap=index(val,0xA0 )) *gap=0;
X		set_var( LEVEL_SET, av[i], val );
X		val="";
X		if( gap ) *gap=0xA0, val=gap+1;
X	}
X	set_var( LEVEL_SET, av[ac-1], val );
X	free(oldval);
X	return 0;
X}
X
Xchar *
Xcopyof( char *str )
X{
X	return strcpy(salloc(strlen(str)+1),str);
X}
X
Xint
Xdo_class( char *avline )
X{
X	CLASS *new;
X
X	if( options&1 ) {
X		avline=next_word(avline);
X		for( new=CRoot,CRoot=NULL; new; new=new->next )
X			Free(new);
X	}
X
X	if( ac==1 ) {
X		for( new=CRoot; new; new=new->next )
X			printf("%s\n",new->name);
X		return 0;
X	}
X
X	avline=next_word(avline);
X	if(!(new=malloc( strlen(avline)+5)))
X		ierror( NULL, 512 );
X	else {
X		new->next=NULL;
X		strcpy( new->name,avline );
X		if( CRoot )
X			LastCRoot->next=new;
X		else 
X			CRoot=new;
X		LastCRoot=new;
X	}
X	return 0;
X}
X
Xdo_getcl( void )
X{
X	char *s=getclass(av[1]);
X	if( s ) printf("%s\n",s);
X	return 0;
X}
X
Xdo_action( char *argline )
X{
X	char *args, err;
X	int abort=options&1;
X
X	args=compile_av( av,3,ac,' ',0 );
X	err=doaction(av[2],av[1],args);
X	if( !abort )
X		if( err==10 )    fprintf(stderr,"Can't identify %s\n", av[2] );
X		else if(err==11) fprintf(stderr,"Can't '%s' this file\n",av[1] );
X	return abort ? !err : err;
X}
END_OF_FILE
if test 28599 -ne `wc -c <'comm3.c'`; then
    echo shar: \"'comm3.c'\" unpacked with wrong size!
fi
# end of 'comm3.c'
fi
if test -f 'csh.doc.aa' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'csh.doc.aa'\"
else
echo shar: Extracting \"'csh.doc.aa'\" \(26142 characters\)
sed "s/^X//" >'csh.doc.aa' <<'END_OF_FILE'
X
X		INSTRUCTIONS FOR SHELL VERSION: 5.10  11-Apr-1991
X		=================================================
X
X  Contents
X  --------
X
X	O.	Installation
X	I.	Description
X	II.	Overview of Major features
X	III.	Restrictions
X	IV.	PIPES
X	V.	Command Pre-processor
X	VI.	Command Line Editing
X	VII.	Function Keys
X	VIII.	Shell Commands
X	IX.	Special Set Variables
X	X.	Functions
X	XI.	Miscellaneous
X	XII.	Example source files
X	XIII.	Default values
X	XIV.	Object oriented features
X	XV.	Keymaps
X
X
X
XO.  Installation
X----------------
X
XYou  can  simply  invoke  csh  from  the  command  line or at the end of your
Xstartup-sequence,  because  csh  is, from the AmigaDOS standpoint, not a true
Xshell  like  l:shell-seg.   Nevertheless,  csh is 'pure', so if your archiver
Xforgot to set the 'p' bit, do it right now.
X
XProposed final setup:  If you want to make csh resident, do the following:
X
XKickstart 1.3
X1. Copy csh anywhere on your (hard)disk, e.g. dh1:tools
X2. Make sure your s:startup-sequence contains:
X	Resident CLI L:Shell-Seg SYSTEM PURE ADD
X	Resident dh1:tools/csh ADD
X	NewCLI FROM s:csh-startup
X	EndCLI
X3. Put the following in s:csh-startup
X	csh -r s:firstlogin.sh
X4. In s:firstlogin.sh, put any commands to be called only once, like setmap,
X   assign, setclock. This is a shell script, use # for comments, and don't
X   use .key type commands. At the end of this file, add
X	source s:login.sh
X5. In s:login.sh, you put any commands that need to be invocated on every
X   invocation, like 'alias', 'set' and so on.
X6. Put the following the s:cli-startup and s:shell-startup, so csh will be
X   started in any window opened by NewCLI or from workbench.
X	csh s:login.sh
X
XKickstart 2.0
X1. Copy csh anywhere on your (hard)disk, e.g. dh1:tools
X2. Make sure your s:startup-sequence contains:
X	Resident dh1:tools/csh ADD
X	csh s:firstlogin.sh
X3. Proceed from step 4 in kickstart 1.3
X
X
XAdditionally,  I  recommend  to  assign CSH:  somewhere and put your docs and
Xcshell  scripts there.  You can do the assign from inside the shell, it's not
Xneeded at startup.
X
X
XI.  DESCRIPTION
X---------------
X
XThis version of Shell is the successor of:
X	Shell V2.04 (C)Copyright 1986, Matthew Dillon, All Rights Reserved
X	Shell V2.04M-V2.07M by Steve Drew
X	Shell V2.08MI, V3.xxA and V4.xxA by Carlo Borreo & Cesare Dieni
X	Shell V5.xxL by Urban Mueller
X
XIf you have any wishes, bug reports or beer bottles, send them to:
X
X	   FIDONET :  AUGL BBS, 2:302/906
X	   INTERNET:  umueller@iiic.ethz.ch
Xor
X	   Urban Mueller
X	   Schulhausstrasse 83
X	CH-6312 Steinhausen
X	   SWITZERLAND
X
XAny sign of intelligent life welcome!
X
XPlease  check the 'restrictions' and 'known bugs' paragraphs before reporting
Xany  bugs.  The support BBS is AUGL, +41 75 8 20 19, +41 75 8 20 18, +41 75 2
X15 87 (all lines USRobotics HST) login='cshell', password='support'
X
XFor  versions prior to 5.00 send suggestions/criticism/anything else to Carlo
XBorreo or Cesare Dieni at:
X
X	   BITNET:  PERUGIA@ICNUCEVM.BITNET
X	   FIDONET: 2:332/602.0@FIDONET
Xor
X	   Carlo Borreo			Cesare Dieni
X	   Via G. Berio 34		Via G. Taddei 3
X	   I-18100 Imperia		I-56100 Pisa
X	   Italy			Italy
X
XYou may distribute this program unmodified and for non-profit only.
X
X*** YOU MAY NOT MODIFY THIS PROGRAM AND REDISTRIBUTE IT ***
X
XIf  everyone  writes his own version of Shell and distributes it, we would be
Xcovered  by  Shell  versions.   If  you make a modification to Shell that you
Xwould  like  to  share  with  other users, please send us a DIFF output, or a
Xdescription of what you need.
X
XCREDITS
X-------
X
XArexx is a program by William Hawes.
XCygnus Ed Professional (C) 1988 CygnusSoft Software.
X
XMany  thanks  to  all  who  gave me feedback, namely Eddy Carroll, Patrizio
XRusconi,  Allard  Siemelink,  Magnus Heldestad, Roddi Walker, Roy Haverman,
XSteve Koren, Randell Jesup and all I forgot to mention.
X
X
XII. OVERVIEW
X------------
X
XShell  provides a convenient AmigaDos alternative command interface.  All its
Xcommands  are  internal  and  thus  does not rely on the c:  commands for any
Xfunctionality.
X
XMajor features include:
X	- command line editing
X	- simple history
X	- piping
X	- aliases with arguments
X	- variables & variable handling (embedded variables)
X	- file name expansion via wild carding ('?', '*' and more)
X	- conditionals (if/else etc..)
X	- source files  (w/ gotos and labels)
X	- tab file name completion
X	- object oriented features (file classes, actions)
X	- many built in commands to speed things up
X	- full functionality on VT terminals
X	- freely programmable command line editing
X
XShell  runs  on  minimum stack, for recursive aliases and source files better
Xincrease stack to 8K or more.
X
XIf you use CShell for the first time, remember the following:
X	- CShell internal commands must be lowercase & can be abbreviated
X	- AmigaDOS command 'Execute' causes some trouble. You cannot redirect
X	  it, you must not rename it, and you get no return code.
X	  You can also use 'source' to start your scripts, but you'll have
X	  to rewrite them a bit. Besides the '.key'-type commands, 'source' 
X	  is downward compatible with 'Execute'.
X	- You can always get more information on a command if csh.doc is
X	  in the current directoy or in csh: (you will be able to modify 
X	  this) and you enter 'man <command>'
X
X
XIII. RESTRICTIONS
X-----------------
X
XThe  following  applies only to Kickstart 2.0, and only to V36:  The INTERNAL
Xcommands cannot be started.  The same is true for the commands in C:  if they
Xwere  made  resident  using  the  AmigaDOS  'Resident' command (with cshell's
X'resident' they work).  Thus, you should disable the INTERNAL residents using
Xthe -i0 startup option if you have a V36 Kickstart.
X
XUnder  1.3, you can't start AmigaDOS residents directly.  But you can use the
X-r  startup  option,  which  will  copy the AmigaDOS resident list to the arp
Xresident  list once (and make those residents unremovable).  I'll try to find
Xa better solution.
X
XThe  best  choice  under  both kickstarts is to make only csh itself AmigaDOS
Xresident, all other commands, if any, ARP resident.  Under 1.3, you can start
Xthe  shell  (as any other commands) from the resident list only after you did
X'resident CLI l:Shell-seg' and 'newcli'.
X
XThe  c:Execute command cannot be redirected, does not return error codes, and
Xmight cause other problems.  It will not work at all if it has been renamed.
X
XThe  VDK:  handler and Frank Seidel's BootRam-Handler have a bug with setting
Xfile  dates,  so  when  using  the  copy command you should try the -d and -p
Xswitches, otherwise your file date will be bad.  (This is not a shell bug)
X
XIf using with conman it you may consider starting shell with the -a switch to
Xturn  off shell's command line editing and use conmans instead.  You'll lose,
Xhowever, many shell features like TAB file name completion.
X
XCB-handler  (a  tool that installs a scrollbar in the CLI window) is not 100%
Xcompatible  with  cshell.   The log will not always represent the real screen
Xcontents.
X
X
XIV. NOTES ON PIPES
X------------------
X
X    PIPES
X	PIPES  have  been implemented using temporary T:  files.  Thus, you
X	should be  careful  when  specifying  a 't:*' expansion as it might
X	include the temporary files. These files are deleted on completion
X	of the pipe segment.
X
X	The file names used are completely unique, even with multiple shell
X	running simultaneously.
X
X	My  favorite  new  feature is the fact that you can now redirect to
X	and  from,  and pipe internal commands.  'echo charlie >ram:x', for
X	instance.  Another favorite:
X
X	echo "echo mem | csh" | csh
X
X	No BCPL program should be output-append redirected (>>).
X
X
XV.  COMMAND PRE-PROCESSOR
X-------------------------
X
X    PREPROCESSING
X	Preprocessing is done on the command line before it is passed on to
X	an internal or external routine:
X
X	^c	where c is a character is converted to that control character.
X		Thus, say '^l' for control-l.
X
X	\233	insert character code 233 octal. Do not use values between
X		200o and 232o, as they have special meanings. \240 is the
X		word separator.
X
X	$name	where name is a variable name.  Variable names can consist of
X		0-9, a-z, A-Z, and underscore (_).  The contents of the
X		specified variable is used.  If the variable doesn't exist,
X		the specifier is used.  That is, if the variable 'i' contains
X		'charlie', then '$i' -> 'charlie'.  If the variable 'i' doesn't
X		exist, then '$i'->'$i' .
X
X	;	delimits commands.   echo charlie ; echo ben.
X
X	' '	(a space). Spaces delimit arguments.
X
X	"string" a quoted string. Trailing quotes are optional. For instance,
X		if you want to echo five spaces and an 'a':
X
X		echo      a       -> a
X		echo "    a"      ->      a
X
X	\c	overide the meaning of special characters.  '\^a' is a
X		circumflex and an a rather than control-a.  To get a backslash,
X		you must say '\\'.
X
X		also used to overide alias searching for commands.
X
X	>file	specify output redirection.  All output from the command is
X		placed in the specified file.
X
X	>>file	specify append redirection (Does not work with BCPL programs).
X
X	<file	specify input redirection. The command takes input from the
X		file rather than the keyboard (note: not all commands require
X		input; it makes no sense to say 'echo <charlie' since
X		the 'echo' command only outputs its arguments).
X
X	|	PIPE specifier.  The output from the command on the left becomes
X		the input to the command on the right.  The current SHELL
X		implimentation uses temporary files to store the data.
X
X	!!	execute the previously executed command.
X	!nn	(nn is a number).  Insert the history command numbered n (see
X		the HISTORY command)
X	!partial search backwards through the history list for a command which
X		looks the same as 'partial', and execute it.
X
X	#	enter comment.  The rest of the line is discarded (note: \#
X		will, of course, overide the comment character's special
X		meaning)
X
X	{e hi;e ho} executes two commands as one, so they can be redirected
X		as one, so they can be redirected together (see ALIAS
X		command). The traling curly brace is optional.
X
X	$(foo)	insert the stdout of the command 'foo' at this position of
X		the command line. Every line of the output will count as one
X		argument. The closing parenthesis is optional.
X
X	`foo`	insert the stdout of the command 'foo' at this position of
X		the command line. Every blank separated word will count as
X		one argument. The trailing backtick is optional.
X
X	Please note the subtle differences between
X		alias count {echo one;echo two}
X		alias count "echo one;echo two
X	The first form will create an alias that contains a local alias. The
X	other will create a plain alias. Also, things within braces will not
X	be parsed more than once, therefore a backslash needs not be pre-
X	ceeded by a pair of backslashes. Thus the commands look the same as
X	if they were to be typed in at the prompt. To echo lots of '#', 
X	either use:
X		forever {echo \#
X		forever "echo \\\#
X	A block can spread over several line. Refer to the SOURCE command.
X
X
XVI. COMMAND LINE EDITING
X------------------------
X
X	o Command line can be up to 255 chars.
X	o Inserts  and  deletes  are handled correctly over multiple screen
X	  lines.
X	o Shell  will  keep  track  of the line width should the window get
X	  resized.
X
X    EDITING
X
X	-- MOVING --
X	Left Arrow	  One character left
X	Right Arrow	  One character right
X	Shift-Left Arrow  One word left
X	Shift-Right Arrow One word right
X	ESC-Left Arrow	  Beginning of line (^A) (^Z)
X	ESC-Right Arrow	  End of line       (^E)
X	-- DELETING --
X	Backspace	  Previous character
X	Del		  Character under cursor
X	ESC-Backspace	  Previous word     (^W)
X	ESC-Del		  Next word    
X	ESC-x-Backspace	  To start of line  (^B)
X	ESC-x-Del	  To end of line    (^K)
X	ESC-d		  Entire line       (^X)
X	-- HISTORY --
X	Up Arrow	  Recall previous commands
X	Down Arrow	  Recall commands
X	Shift-Up Arow	  Get history from partial (or number)
X	Shift-Down Arrow  Go below last command of history
X	ESC-Up Arrow	  Get start of history
X	ESC-Down Arrow	  Get end of history
X	ESC-!		  Get history from partial (or number)
X	^T		  Insert tail (all but first word) of previous line
X	^P		  Duplicate previous word (useful for mv)
X	-- COMPLETION --
X	TAB		  Inserts first matching file name
X	Shift-TAB	  Inserts longest common substring
X	ESC-TAB		  Inserts all matching file names
X	ESC-c		  Does a quick cd on left word (TAB for cycling)
X	ESC-~		  Inserts the last current directory
X	-- EXECUTING LINE --
X	Return		  Executes line
X	ESC-Return	  Executes this line of history & brings up next one
X	^N		  Next line. Don't exec this one but store history
X	^\		  EOF (directly exits)
X	-- MISCELLANEOUS --
X	^L		  Retype current line.
X	^O		  Echo a ^O
X	^R		  Repeat last command (don't play with this)
X	^U		  Undo/Redo last edit 
X	ESC-i		  Toggle Insert/Overwrite
X	f1-f10		  Execute command if variable exists.
X	F1-F10		  More commands (Shifted f keys).
X	Help		  Invokes help command
X
XWhenever  the cursor is placed on or directly after an incomplete file name
Xand   you  press  TAB,  CShell  inserts  for  the  first  filename  (sorted
Xalphabetically)  that  matches  the name part already typed.  Any wildcards
Xare allowed here, if none are given, '*' is appended.  Immediately pressing
XTAB  again  brings  up  the  next  file  name  that  matched the substring.
XShift-TAB  will  only  insert  the  as  much as is common to all files that
Xmatched  you  abbreviation.   If  pressed  again,  behaves  just  like TAB.
XESC-Tab  inserts  the  name  of the directory where you would have ended up
Xwith a quick cd to that substring.
X
XNote  that  ^D  now  means  'quit  batchfile' like under AmigaDOS and is no
Xlonger used for quitting CShell. If you want it back, enter 'keymap 0 4=41'
X
XThe  CTRL  keys FGVY are unset, feel free to map them to any function (see
Xchapter XV).  You can also remap all preset keys.
X
XVII. FUNCTION KEYS
X------------------
X
X    FUNKEY
X	Function keys now insert text to the current position on the command
X	line. They maybe terminated with a ^M (return). f1 would be non shifted
X	where as F1 is shifted.
X	Most of functions key have a default definition, but it may be changed.
X
X		$ set f1 dir df0:^M
X
X	will add the text 'dir df0:<return>' to the current line.
X
X		$ set f1 dir
X
X	would only add 'dir' you could then enter ' df0:<return>'
X
X
XVIII. SHELL COMMANDS
X--------------------
X
X    STARTUP OPTIONS
X	First to start shell from a CLI:
X
X	shell [-abcfiknstv] [-c command;command]
X	shell [-abcfiknstv] [batchfile1 ... batchfileN]
X
X	 -a  AUX: mode. No command line editing and text highlighting
X	 -b  starts shell in background, which means only task priority -1.
X	 -c  allows execution of one command line and then exits out
X	     of  shell.   This  is  useful for running an internal shell
X	     commands in the background or from an external application:
X		Run csh -c "dir df0:; copy -r df0: df1: >nil:; echo Done"
X	 -f  starts shell in foreground, which means only task priority 1.
X	     you might reset this prioritiy to 0 at the end of your .login
X	 -i0 disables INTERNAL residents. for V36 kickstarts.
X	 -k  sets _nobreak before doing anything
X	 -n  suppresses starting of s:.login
X	 -r  copies the amiga resident list to the arp resident list. You
X	     can't remove them anymore. No copying when under kick 2.0 or
X	     if arp residents present.
X	 -s  globally enables the star '*' as alias for #? in AmigaDOS 2.0
X	 -t  terminal mode. You can use command line editing and text high-
X	     lighting on a VT100 compatible terminal.
X	 -v  sets _verbose to 'hs' before doing anything.
X
X	Under 1.3, the best thing you can do is to make csh resident using
X	the AmigaDOS Resident command in the startup-sequence, then start
X	it with the option -r, so you can start another resident csh from
X	inside csh. NOTE: Residents copied using -r cannot be removed! I
X	advise you to use the internal resident command for all but csh
X	itself.
X
X    COMMAND EXECUTION
X
X	Internal shell commands are case sensitive and may be abreviated.
X
X   The first argument is the command-name... here is (in order) how Shell
X   tries to execute it:
X
X	1)  The alias list is searched for an alias with an exactly 
X	    matching name.
X	2)  Internal commands list is scanned for a command even partially
X	    matching name (so you can, for instance, say resi for resident;
X	    however, you should specify enough of a command to be unique).
X	3)  Then, the list of functions is scanned for a command that
X	    matches completely. If one is found, the result of the function
X	    is echoed to stdout.
X	4)  Now the command is assumed to be external. Arguments with spaces
X	    or empty strings will be surrounded by quotes.
X	5)  If the file is a directory, a 'cd <file>' will be performed to
X	    it.
X	6)  AmigaDOS and ARP resident list are scanned for it (you can use
X	    Shell's 'resident' command to add/remove a file in the ARP list).
X	7)  If the file is in the current directory and it's executable, it
X	    is started.
X	8)  Then it is searched in the AmigaDOS path and c: (NOTE: Path
X	    assigns to C: under Kickstart 2.0 don't work; use 'path')
X	9)  Now, the shell path ($_path) is searched. If it's found and 
X	    executable, it's be started. If it has the 's' bit set, it will
X	    be 'c:Execute'd.
X	10) If there exists a file with the suffix '.sh' and the same root
X	    in the current directory or in the shell path, it is 'source'd.
X	11) Then the variable _rxpath is examined. If there exists a file
X	    with the suffix '.rexx' and the same root in the current direc-
X	    tory or in '$_rxpath', 'RX <file>' will be performed.
X	12) If all failed, an 'exec' action is sent to the file. See chapter
X	    XIV for more info on classes and actions.
X
X	To enforce that the external 'dir'-command is used, enter 'Dir'. Tt
X	is a good habit to uppercase the first letter of all external com-
X	mands, even if this is not necessary.
X
X	AUTOMATIC SOURCING may be accomplished by naming shell scripts with
X	a  .sh  suffix.   Thus,  if you say 'stuff' and the file 'stuff.sh'
X	exists in your current or anywhere in Shell search path (NOTE: won't
X	be found in the AmigaDOS path), it will be SOURCED  with all arguments
X	you  have given placed  in  the  $_passed variable. This is equivalent
X	to typing 'source stuff.sh'
X
X    WILD CARD EXPANSION
X	Most shell commands will accept multiple arguments that can
X	be as a result of wild card expansion.  Also when calling
X	an external command shell will first expand any wild cards
X	to separate arguments.  If you wish to have the external command
X	handle it's own wild carding you will need to insert quotes
X	around the special wild card characters or use an alias (see
X	explanation of 'alias')
X
X	eg.
X		arc a new.arc *.txt	- shell will expand and pass to arc
X		arc a new.arc "*.txt"	- let arc expand the wild cards.
X		alias arc "*a Arc $a"	- now shell will never expand
X
X	Wildcards allowed:
X
X	?	match any single character
X	*	match any string
X	.../*	recursive search down ALL sub directories from current level
X	~	exclude pattern matching specifier
X	!	synonym for ~, supported for compatibility
X	&	prefixed to patterns, ask confirmation for each file
X	[]	character class
X	~	the previous current directory (if separated)
X	Note that a pattern must contain a '?' or a '*', otherwise the other
X	special characters are not recognized. Furthermore, you cannot spe-
X	cify a single '?' as a pattern in the first argument to a command,
X	as this will be passed on to the command in order to show its usage.
X
X	Examples:
X
X	df0:.../*	all files in all directories on df0:
X	df0:.../!*.info	full directory tree of df0: but exclude
X			any ugly .info files.
X	!*.o !*.c	will result in ALL files matching since what
X			doesn't match the !*.o will match the !*.c
X	df1:&*		all files in root of df1:, but ask 
X			confirmation for each
X	*.[co]		all files ending in .c or .o
X	~*.[co]		all files NOT ending in .c nor in .o
X	~		the previous current directory
X	~/*.c		all .c files in the previous current directory
X	~//		the parent of the previous current directory
X	.		the current directory
X	./foo.c		the same as foo.c
X	..		the parent of the current directory
X	../foo.c	the file foo.c in the parent directory
X
X	Note that some commands prevent wild card expansion. These are:
X	  - dir, rpn, whereis, window
X	Those commands will expand the wild cards themselves. This is why
X	  dir @without( *.? , *.o )
X	will not work. Instead use:
X	  set arg @without( *.? , *.o );dir $arg
X
X	There is one exception to the rules given above: A single '?' as
X	the first argument will *not* expand in order to allow you to get
X	the usage of CLI and csh commands easily.
X
X	The   following   symbols  are  not  yet  supported  by  wild  card
X	expansions, but are accepted in search -w:
X
X	( | )	OR matching
X	#	0 or more times the pattern following
X
X	Examples:
X
X	"k#a"		matches ka, kaa, kaaa, etc.
X	"hel(lo|p)"	matches hello or help.
X
X
XLIST OF COMMANDS:
X-----------------
X
X    ABORTLINE
X	Usage	: abortline
X	Example	: echo a;abort;echo b
X	Results	: a
X
X	Causes the rest of the line to be aborted. Intended for use in
X	conjunction with exception handling.
X
X    ACTION
X	Usage	: action [-a] actionname file [arguments]
X
X	Sends an action to a file. See chapter XIV CLASSES
X	Options:
X	 -a (abort) returns 0 if failed and 1 if successful. Otherwise,
X	            normal error codes (10 or 11) are returned
X
X    ADDBUFFERS
X	Usage	: addbuffers drive buffers [drive buffers ...]
X	Example	: addbuffers df0: 24
X
X	Just like AmigaDOS addbuffer command, causes new buffers to be
X	allocated for disk I/O. Each buffer costs 512 bytes of memory,
X	CHIP memory if a disk drive.
X
X    ALIAS
X	Usage	: alias [name [command string] ]
X	Example	: alias vt "echo Starting VT100;run sys:tools/vt100"
X
X	Sets a name to be a string. You can alias a single name to a set
X	of commands if you enclose them in quotes as above. By simply
X	typing vt, the command line above would be executed.
X	Aliases may call each other, but direct recursion is prohibited,
X	so the following works: alias ls "ls -s"
X	To prevent alias replacement, enter: \ls
X
X	By typing "alias name", you will get the alias for that name, while
X	with "alias" you get a list of all alias.
X
X	ARGUMENT PASSING TO AN ALIAS:
X
X	Usage	: alias name "%var[%var...] [ command_string ]"
X		  alias name "*var[%var...] [ command_string ]"
X	Example	: alias xx "%q%w echo hi $q, you look $w
X		  xx Steve great today
X	Results	: hi Steve, you look great today
X
X	The second form of the alias command allows passing of arguments
X	to any position within the command string via use of a variable
X	name. To pass arguments to the end of a command string this method
X	is actually not necessary. These variables are local, so they don't
X	destroy another variable with the same name.
X	If you specify multiple arguments, every argument will be assigned
X	one word, and the last argument will be assigned the rest of the
X	command line.
X
X	Using a '*' instead of the first '%' prevents wild card expansion:
X		alias zoo "*a zoo $a
X	To expand the wild cards after you got them, use
X		exec set a $a
X
X	IMPLICIT ALIASES:
X
X	Usage	: {command;command}
X		  {%var command;command} arg arg
X	Example	: {%tmp echo $tmp $tmp} hello
X	Results	: hello hello
X
X	Curly braces define a temporary aliases. They can be redirected as
X	a whole, can have arguments and local variables. They count as
X	one argument, even if there are blanks inside (as with quotes), and
X	they may be nested. Complex alias definitions can use the braces
X	instead of quotes, although this will add some calling overhead.
X	The closing curly brace is optional if at the end of line.
X	Example:
X
X	alias assert {e "Are you sure? ";input -s sure
X
X    ASCII
X	Usage	: ascii
X		  ascii string
X
X	If called without arguments, ascii outputs a complete ascii table.
X	Given a string, shows each character in ascii. Options:
X	  -h shows numbers in hexadecimal
X	  -o shows numbers in octal
X
X    ASET
X	Usage	: aset name value
X	Example	: aset INCLUDE include:
X
X	Set a variable in a way that is compatible with ARP/old Aztec set
X	command; this is completely different from ENV: Shell variable.
X
X    ASSIGN
X	Usage	: assign
X		  assign logical
X		  assign [-lnp] logical1 physical1 [logical2 physical2 ... ]
X
X	The first form shows all assigns.
X	The second form kills one assign.
X	The third form assigns logical1 to physical1 and so on. Options:
X	 -l creates a late-binding assign under kick 2.0, normal otherwise
X	 -n creates a non-binding assign under kick 2.0, normal otherwise
X	 -p creates a path-assign under kick 2.0, cancelled otherwise
X	For definition of late/nonbinding, refer to your AmigaDOS manual.
X
X    BASENAME
X	Usage	: basename var path
X	Example	: basename x df0:c/Dir	# sets x to "Dir"
X
X	Sets var specified to basename of path.
X
X    CAT
X	Usage	: cat [-n][file file....]
X	Example	: cat foo.txt
X
X	Type the specified files onto the screen.  If no file is specified,
X	STDIN in used (note: ^\ is EOF).  CAT is meant to output text files
X	only. Specifying -n option you will get numbered lines.
X
X    CD
X	Usage	: cd [path]
X		  cd -g device1 [device2 [device3 ...]]
X
X	Change your current working directory.  You may specify '..' to go
X	back one directory (this is a CD specific feature, and does not
X	work with normal path specifications).
X
X	In most cases, you'll no more have to use the CD command. Just type
X	the desired directory at the prompt (very handy in conjunction with
X	file name completion). Typing a ~ alone on a command line cd's
X	to prevous current directory.
X
X	There are two situations left when you still need it:
X
X	Entering 'cd *tem' will cd to the first name matched.
X
X	The second form generates a list (an ascii file) of all direc-
X	tories on the given devices. It will be stored in the file given
X	in $_qcd (default: 'csh:csh-qcd'). Note that this ascii file  will
X	not be merged but overwritten. Once you have generated this file,
X	you can cd to any directory on your harddisk(s) even if it's not
X	in the current directory.
X	If you have two directories of the same name and you use one of
X	them more, move the more important one to the beginning of the
X	qcd file. You might also sort the file.
X	It is legal to type just an abbreviation of the directory name
X	you want to cd to. No asterisk '*' necessary. If you end up in
X	the wrong directory, cd to the same directory again (best done
END_OF_FILE
if test 26142 -ne `wc -c <'csh.doc.aa'`; then
    echo shar: \"'csh.doc.aa'\" unpacked with wrong size!
fi
# end of 'csh.doc.aa'
fi
echo shar: End of archive 4 \(of 6\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mail submissions (sources or binaries) to <amiga@uunet.uu.net>.
Mail comments to the moderator at <amiga-request@uunet.uu.net>.
Post requests for sources, and general discussion to comp.sys.amiga.misc.