[comp.sources.amiga] v02i098: sh - unpack shar files

page@swan.ulowell.edu (Bob Page) (12/13/88)

Submitted-by: guilford@turing.cs.rpi.edu (Jim Guilford)
Posting-number: Volume 2, Issue 98
Archive-name: unix/sh.1

[uuencoded binary included - it's small.  ..Bob]

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file"
# Created Tue Nov 29 09:00:48 1988
#
# This archive contains:
#		NOTES
#		README
#		sh.c
#		sh.uu
echo "Creating NOTES"
cat > NOTES <<"***EOF NOTES***"
Here is yet another version of "sh" -- a program to unshar file from
over the net. I wrote it from my own need--I found existing programs
to behave poorly (not understand formats, guru, etc.) I put into it
the features that were important to me (and left out the ones I didn't
need :-). Particularly, I intended it to be expandable to different
formats. At this point it understands about cat and a simple (and
common) form of sed. Adding new forms should be easy (although I have
not had needed to do this myself to unshar files I've found).

It should not need a lot of stack space to run.

I've built into it a variety of options so that it can be customized
for individual preferences. These allow one to decide how verbose its
messages are or how paranoid it is about strange forms.

I have not done extensive testing on it (remember, I originally wrote
this for myself), but I have been using it for the past six months
with no problems.

I have not included a "shar" program with this, as I found the
existing one(s) to work acceptably well.
***EOF NOTES***
echo "Creating README"
cat > README <<"***EOF README***"
SH -- by Jim Guilford

This is my version of a utility to unshar files. I wrote it as I found
existing programs to be unreliable and limited. This works to the best
of my knowledge, but I make no guarrantees.

The program knows four simple sh commands: echo, cat, sed, and exit.
The version of sed that I've implemented is SEVERELY limited, but does
manage to unshar all of the shar files I've seen which use sed. If sh
finds any other commands, it ignores them.

Sh is invoked as
sh [options] [filename]
If the filename is omitted, the standard input is taken. The options
can be given individually (e.g. -s1 -o1), or combined (e.g. -s1o1).
The options are as follows:
-v0 == verbosity of zero. Don't print what isn't understood
-v1 == verbosity of one. Print what isn't understood
-v2 == verbosity of two. Print what isn't understood and commands as
       they are executed. This will only echo the cat and sed
       commands. I thought that echoing the 'echo' command was rather
       silly, as is echoing 'exit'.
-v  == without a number is defaults to -v1
-s0 == safety off. Continue if something is not understood
-s1 == safety on. Stop the first time something is not understood (in
       case you are paranoid).
-s  == defaults to -s1
-o0 == overwrite off. Don't check for overwriting existing files.
-o1 == overwrite on. Don't overwrite files if they already exist
-o  == defaults to -o1

The defaults as compiled into the program are:
-v1 -s0 -o1

Known bugs:
  In evaluating tokens, I handle quotes properly. The only escape
character for which I check, however, is the back-slash, '\'. This is
removed, and the next character is taken as is. If a token contains
any other escape characters, then it won't be parsed properly.  If
this happens to the token following the '<<', then essentially all of
the shar file will be dumped into the first file that was to be
unshared.  This is a good indication that funny characters are around.
If this happens, then the easiest way to fix it is to zip into your
favorite editor and change one of the pair of terminating string to
match the other. This can usually be done with one global search and
replace (once you know the changes to be made). 

Programmer's Notes:
  The program is designed to be modular and easily enhanced to
understand a new shar form. It currently understands the following:
cat << some-string > file-name
cat << some-string >> file-name
sed s/^X// << some-string > file-name
sed s/^X// << some-string >> file-name

where the sed quote character (e.g. '/') can be anything and the sed
'X' character can be anything. Additionally, the ordering of the
arguments is unimportant as is whether a space follows the indirection
operators. In other words, this would also be accepted:
cat >file-name <<"some string"

Just as a side note, it was compiled under lattice 4.0. It should be
fairly portable, but I make no guarrantees.

If you have any comments, suggestions, or you just want to say hi, I
can be reached at:
guilford@turing.cs.rpi.edu
guilford@csv.rpi.edu
...!rutgers!nysernic!rpics!guilford

***EOF README***
echo "Creating sh.c"
cat > sh.c <<"***EOF sh.c***"
/* sh - JimG's version of sh to unshar files */
char *help = "\
Usage: sh [-v(012)] [-s(01)] [-o(01)] [filename]\n\
  -v = verbosity\n\
	0 = print nothing\n\
	1 = print anything not understood\n\
	2 = echo commands as they are executed\n\
  -s = safety\n\
	1 = exit when something is not understood\n";
char *help2 = "\
  -o = overwrite\n\
	1 = don't overwrite existing files\n\
  filename\n\
	read shar file from here (or stdin if missing)\n";

#define NDEBUG

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define ST_CMDLINE	1
#define ST_CAT		2
#define ST_SED		3

#define TST_OUT		1
#define TST_QUOTE	2
#define TST_ALPHA	3
#define TST_DIR		4

#define BUFSIZE 161
char buf1[BUFSIZE], buf2[BUFSIZE];
char *buff1end = buf1 + BUFSIZE;

unsigned short int verbosity = 1;
unsigned short int safety = 0;
unsigned short int overwrite = 1;

FILE *fopen(char*,char*);
void sh(FILE*);
int tokenize(char**,int,char*);
void bumpstr(char*);
void ind_error(char**,int);
void fileparse(char**,int,char*,FILE**);
int fillbuffer(char*,int,FILE*);
int match(char*,char*);
void ptokens(char**,int,FILE*);

int
fillbuffer(buf,len,fp)		/* This will read up to len-1 characters */
char *buf;
int len;
FILE *fp;
{
	register int i, c;
	
	for (i=1; i<len; i++) {
		c = getc(fp);
		if (c == EOF) break;
		*(buf++) = c;
		if (c == '\n') break;
	}
	*buf = 0;
	return (c);
}

main(argc,argv)
int argc;
char *argv[];
{
	register FILE *fp;
	register char *s;
	
	while ((--argc > 0) && ((*++argv)[0] == '-'))
	    for (s = argv[0]+1; *s != '\0'; s++)
		switch (*s) {
		case 'v':
			switch (s[1]) {
			case '0':
			case '1':
			case '2':
				verbosity = (*++s) - '0';
				break;
			default:
				verbosity = 1;
				break;
			}
			break;
		case 's':
			switch(s[1]) {
			case '0':
			case '1':
				safety = (*++s) - '0';
				break;
			default:
				safety = 1;
				break;
			}
			break;
		case 'o':
			switch(s[1]) {
			case '0':
			case '1':
				overwrite = (*++s) - '0';
				break;
			default:
				overwrite = 1;
				break;
			}
			break;
		default:
			fputs("sh: illegal option ",stderr);
			putc(*s,stderr);
			putc('\n',stderr);
			exit(10);
			break;
		}
	switch (argc) {
	case 0:	sh(stdin);
		break;
	case 1:	fp = fopen(argv[0],"r");
		if (fp == NULL) {
			fputs("Error - can't open file: ",stderr);
			fputs(argv[0],stderr);
			putc('\n',stderr);
			exit(10);
		}
		sh(fp);
		fclose(fp);
		break;
	default:
		fputs(help,stderr);
		fputs(help2,stderr);
		exit(5);
	}
	return (0);
}

int
match(pat,str)
register char *pat, *str;
{
	while (*pat != '\0')
		if (*pat++ != *str++) return (0);
	return(1);
}

void
sh(fp)
FILE *fp;
{
    int state = ST_CMDLINE;
    int oldc, c, tcnt;
    FILE *outfp;
    char *tokens[20];
    char schar;

    oldc = '\n';
    while ((c = fillbuffer(buf1, BUFSIZE, fp)) != EOF ) {
	switch (state) {
	case ST_CMDLINE:
		if (oldc != '\n') break;
		if (*buf1 == '#') break;
		tcnt = tokenize(tokens, 20, buf1);
		if (tcnt == 0) break;
		if (strcmp(tokens[0],"echo") == 0) {
			ptokens(tokens+1,tcnt-1,stdout);
		} else if (strcmp(tokens[0],"cat") == 0) {
			fileparse(tokens,tcnt,buf2,&outfp);
			if (*buf2) {
				state = ST_CAT;
				if (verbosity == 2) {
					fputs("I am executing:   ",stdout);
					ptokens(tokens,tcnt,stdout);
				}
			} else if (outfp != stdout) fclose(outfp);
		} else if (strcmp(tokens[0],"sed") == 0) {
			fileparse(tokens,tcnt,buf2,&outfp);
			if (*buf2) {
			    if (verbosity == 2) {
				fputs("I am executing:   ",stdout);
				ptokens(tokens,tcnt,stdout);
			    }
			    if ((tokens[1][0] == 's') &&
				(tokens[1][1] == tokens[1][4]) &&
				(tokens[1][2] == '^') &&
				(tokens[1][5] == tokens[1][1])) {
				    schar = tokens[1][3];
				    state = ST_SED;
			    } else {
				fputs("Warning -- unknown sed option\n",stderr);
				fputs(tokens[1],stderr);
				fputs("\nI am treating it as a 'cat'\n",stderr);
				fflush(stderr);
				state = ST_CAT;
			    }
			} else if (outfp != stdout) fclose(outfp);
		} else if (strcmp(tokens[0], "exit") == 0) {
			return;
		} else {
			if (verbosity > 0) {
/*			  fputs("I don't understand and am ignoring:\n",stdout);*/
			  fputs("Don't understand: ",stdout);
			  ptokens(tokens,tcnt,stdout);
			}
			if (safety == 1) exit(5);
		}
		break;
	case ST_CAT:
		if ((oldc == '\n') && (match(buf2,buf1))) {
			state = ST_CMDLINE;
			if (outfp != stdout) fclose(outfp);
		} else fputs(buf1,outfp);
		break;
	case ST_SED:
		if ((oldc == '\n') && (match(buf2,buf1))) {
			state = ST_CMDLINE;
			if (outfp != stdout) fclose(outfp);
		} else if ((oldc == '\n') && (*buf1 == schar))
			fputs(buf1+1,outfp);
		else fputs(buf1,outfp);
		break;
	default:
		fputs("\nInternal Error -- unknown state!\n",stderr);
		exit(20);
	}
	oldc = c;
    }
}

void
ptokens(tokens, size, fp)
char *tokens[];
int size;
FILE *fp;
{
	register int i;
	
	for (i = 0; i < (size - 1); i++) {
		fputs(tokens[i],fp);
		putc(' ',fp);
	}
	if (size > 0) fputs(tokens[size - 1],fp);
	putc('\n',fp);
	fflush(fp);
}

int
tokenize(tokens, tsize, buf)
char *tokens[];
int tsize;
char *buf;
{
	register char c;
	char quote;
	int tcnt;
	char *tbuf;
	int state;

	tcnt = 0;
	state = TST_OUT;
	for (; ((c = *buf) != NULL); buf++) {
	    switch (state) {
	    case TST_OUT:
		switch (c) {
		case ' ':
		case '\t':
		case '\n':
			break;
		case '\'':
		case '"':
		case '`':
			quote = c;
			state = TST_QUOTE;
			if (tcnt >= tsize) return(tsize);
			tokens[tcnt++] = tbuf = (buf + 1);
			tbuf++;
			break;
		case '<':
		case '>':
			state = TST_DIR;
			if (tcnt >= tsize) return(tsize);
			tokens[tcnt++] = tbuf = buf;
			tbuf++;
			break;
		case '\\':
			buf++;
		default:
			state = TST_ALPHA;
			if (tcnt >= tsize) return(tsize);
			tokens[tcnt++] = tbuf = buf;
			tbuf++;
			break;
		} /* end of switch (c) */
		break;
	    case TST_QUOTE:
		if (c == quote) {
			*buf = 0;
			state = TST_OUT;
		} else {
/*			*tbuf++ = c; */		/* Currently no-op */
		}
		break;
	    case TST_DIR:
		if ((c != '>') && (c != '<')) {
			if ((c != ' ') &&
			    (c != '\t') &&
			    (c != '\n')) bumpstr(buf);
			*buf = 0;
			state = TST_OUT;
		} else {
/*			*tbuf++ = c; */		/* Currently no-op */
		}
		break;
	    case TST_ALPHA:
		if ((c == ' ') ||
		    (c == '\t') ||
		    (c == '\n')) {
			*tbuf = 0;
			state = TST_OUT;
		} else if (c != '\\') {
			*tbuf++ = c;
/*			*tbuf++ = c; */		/* Currently no-op */
		}
		break;
	    } /* end switch (state) */
	}
	return (tcnt);
}

void
bumpstr(buff)		/* buff has to be a pointer in buf1 !!!!! */
register char *buff;
{
	register char c1, c2;
	
	assert(((buff >= buf1) && (buff < buff1end)));

	c1 = *buff++;
	while (buff != buff1end) {
		c2 = *buff;
		*buff++ = c1;
		if ((buff == buff1end) || (c1 == 0)) break;
		c1 = *buff;
		*buff++ = c2;
		if (c2 == 0) break;
	}
}

void
ind_error(tokens, tcnt)
char *tokens[];
int tcnt;
{
	fputs("Error in indirection -- ignored\n",stderr);
	ptokens(tokens,tcnt,stderr);
}

void
fileparse(tokens, tcnt, buf, outfp)
char *tokens[];
int tcnt;
char *buf;
FILE **outfp;
{
	int i;

	*buf = 0;
	*outfp = NULL;
	
	for (i=0; i < tcnt; i++) {
	    if (strcmp(">",tokens[i])==0) {
		if (*outfp) ind_error(tokens,tcnt);
		else if (++i == tcnt) ind_error(tokens,tcnt);
		else {
			if (overwrite == 1) {
			    *outfp = fopen(tokens[i],"r");
			    if (*outfp != NULL) {
				fputs("Warning: will not overwrite: ",stderr);
				fputs(tokens[i],stderr);
				putc('\n',stderr);
				fflush(stderr);
				fclose(*outfp);
				tokens[i]="nil:";
			    }
			}
			*outfp = fopen(tokens[i],"w");
			if (*outfp == NULL) ind_error(tokens,tcnt);
		}
	    } else if (strcmp(">>",tokens[i])==0) {
		if (*outfp) ind_error(tokens,tcnt);
		else if (++i == tcnt) ind_error(tokens,tcnt);
		else {
			*outfp = fopen(tokens[i],"a");
			if (*outfp == NULL) ind_error(tokens,tcnt);
		}
	    } else if (strcmp("<<",tokens[i])==0) {
		if (*buf) ind_error(tokens,tcnt);
		else if (++i == tcnt) ind_error(tokens,tcnt);
		else strcpy(buf,tokens[i]);
	    }
	} /* end for */
	if (*outfp == NULL) *outfp = stdout;
}

***EOF sh.c***
echo "Creating sh.uu"
cat > sh.uu <<"***EOF sh.uu***"

begin 644 sh
M```#\P`````````#``````````(```9A```#%P```@0```/I```&84CG?OY++
M[P`T)$@D`$GY`````"QX``0I3@!`*4\`3$*L`$B3R4ZN_MHF0"EK`)@`.$JK]
M`*QG``!P(`V0K0`$!H````"`*4``!&$``7H@:P"LT<C1R")H`!#3R=/)(`)R/
M`!(9*4D`5-"!4H!"9U*``D#__I_`58!"=P@`(`)3@-2!'[(``"``4X)1R/_VA
M'[P`("``4X(?L2``(`!1RO_X(D\O"6```&PI:P`Z``0&K````(``!&$``0YA@
M``#X*4``2"\`)$`@*@`D9Q(L;`2H($`B*```*4$`.$ZN_X(B*@`@9QHD/```]
M`^U.KO_B*4``4&<*Y8@@0"=H``@`I"!L`$@O"$AL```@:``D*6@`!`!41_D`3
M``2\<@`@/````-5@`B;!4<C__$ZZ$;QP`&`$("\`!"\`("P`+&<$($!.D$ZZ4
M!\(L>``$(FP$J$ZN_F)*K`2P9P@B;`2P3J[^8DJL!+1G"")L!+1.KOYB2JP`S
M6&<((FP`6$ZN_F)*K`!(9R0B+``\9P1.KO_<(BP`4&<$3J[_W"QX``1.KO]\=
M(FP`2$ZN_H8@'RYL`$Q,WW]^3G5P9&"`0>L`7$ZN_H!!ZP!<3J[^C$YU0^P`<
M7'``3J[]V"E`!*AGVDYU``!(YS`R+'D```2L(&\`&")O`!PD;P`@)F\`)"`OK
M`"@B+P`L)"\`,"8O`#1.KOZD3-],#$YU3E7__$CG(`!P`"E``!A*K0`(:R0DE
M+0`(M*P$%&P:(@+G@4'L!M`B2-/!2I%G"B("YX'1P2`(8`AP"2E``W!P`$S?J
M``1.74YU`"/D9P`CVP```````1(`(]M>```RW``C3E7_^"\M``A.NO^66$\KK
M0/_X2H!F!'#_8#8@;?_X""@``@`#9P9P`""`8"1"K?_\+R@`!$ZZ%=)83TJL[
M`!AG!G#_*T#__"!M__A"D"`M__Q.74YU2JP$K&820^P#7'``+'@`!$ZN_=@ID
M0`2L*6P`5`,02'@`*$AX`/IP`"\`+P!(;`-(+P!(;`,N+P!.NO[>3^\`($AX@
M`!1.NA,"6$].=2`M__Q.74YU`"0-*$Y5__1(YP`@1>P#?+3\``!G-@@J``(``
M&V8J""H``0`;9R(@*@`$D*H`$"M`__A*@&<2+P`O*@`0+RH`'$ZZ"B)/[P`,*
M)%)@Q"\M``A.NA*B6$],WP0`3EU.=0``Y+L`````<&%.5?_T(&T`"`@H``$`*
M&V<2+PA(>/__3KH+]%!/*T#__&`&<``K0/_\(&T`""`H`!@"@`````Q*@&848
M2J@`%&<.+R@`%"\H`!!.N@@J4$\@;0`(+R@`'$ZZ_IA83RM`__@,K?______)
M_&<$2H!G!'#_8`)P`$Y=3G5.5?_X0>P#?"M(__Q*K?_\9QH@;?_\2J@`&&<0[
M*VW__/_X(&W__"M0__Q@X$JM__QF+$AX`").N@*$6$\K0/_\2H!F!'``8"@@5
M;?_X(*W__'`A<@`@;?_\$,%1R/_\+RW__"\M``PO+0`(80A/[P`,3EU.=4Y5#
M_^X@;0`02J@`&&<(+PA.NO\"6$\K;`-X__0K;0`,__`@;?_P$"@``0)``/\,$
M0`!B9PP,0`!A9A)"K?_T8`@K?```@`#_]%*M__`@;?_P#"@`*P`!5\!$`$B`+
M2,`@;0`,$A`"00#_&T#_[PQ!`'=G``":#$$`<F=*#$$`868``-Y(>``,+SP`L
M`($"+RT`"$ZZ!*I/[P`,*T#_^%*`9@9P`&```/Q*+?_O9P@@/````(!@`G`"7
M`(```$``*T#__&```*!*+?_O9P1P`F`"<```@```@`!(>``,+P`O+0`(3KH$E
M6D_O``PK0/_X4H!F!G``8```K$HM_^]G""`\````@&`"<`$K0/_\8%9*+?_O$
M9P1P`F`"<`$`@```@```@````0``@````@!(>``,+P`O+0`(3KH$!D_O``PK9
M0/_X4H!F!'``8%A*+?_O9P@@/````(!@`G`"*T#__&`$<`!@/I'((FT`$"-(:
M`!`C2``4(VW_^``<(VD`$``$(T@`#"-(``A*K?_T9P0@"&`&(#P``(``(BW_)
M_(*`(T$`&"`)3EU.=0`CW6@``'!A3E4``"!M``P(*``&`!MG&"(M``@,@0``Z
M``IF#"\(+P%.N@E24$]@/"!M``Q3J``,("@`#$J`:Q0B:``$4J@`!"`M``@2Q
M@'(`$A%@%B`M``@"@````/\O""\`3KH)&%!/(@`@`4Y=3G5.5?_\<``@;0`(C
M$!!2K0`(*T#__$J`9Q0O+0`,+P!.NO]X4$]2@&;<</]@`G``3EU.=0``3E4`M
M`"\M``AA!EA/3EU.=4Y5_^Q(YP,@+BT`"$J';@9P`&```,0,AP````AL`GX(+
M(`<@!U:`Y(#E@"X`0>P$#"10*TC_^+3\``!G3B(J``2RAVT^LH=F$B!2(FW_`
M^"*(GZP$$"`*8```@"`J``20APR`````"&T:($H@2M''()(A0``$(FW_^"*(^
MGZP$$"`*8%8K2O_X)%)@K"`'(BP$N"`'T(%3@$ZZ#;0B+`2X3KH.B%"`+``@V
M!B`&5H#D@.6`+``O!DZZ`'Y83RM`__!*@&<4+P8O`$ZZ!()03R\'80#_,%A/V
M8`)P`$S?!,!.74YU`&%F``#>2'@`#"\\3E7_]B\M``A.NOI66$\K0/_V2H!F6
M!'#_8"HO+0`0+RT`#"!M__8O*``$3KH/O$_O``PK0/_Z2JP`&&<$</]@!"`M(
M__I.74YU3E7_^"`M``@&@`````PO0```("\``'(`+'@`!$ZN_SHK0/_\2JW_1
M_&8$<`!@-"`M``@&@`````P@;?_\(4``""\(2&P&`&$``0A03TJL!`!F!BEMX
M__P$`"!M__S0_``,(`A.74YU3E7__"\M``AAD%A/*T#__$J`9@8P?/__(`A.+
M74YU3E7_^$CG`2!A``"`<``I0``0*4``""E```PI0`0,*4`$$"E`!`0I0`0`.
M*4`$"$JL`_QG3"`L!+@B+`/\TH!3@2`!(BP$N$ZZ#$PB+`2X3KH-(%"`+@`@`
M!R`'5H#D@.6`+@`O!V$`_Q983R1`M/P``&8$</]@#"\'+PI.N@+\4$]P`$S?6
M!(!.74YU3E7_^"ML!@#__$JM__QG)"!M__PK4/_X(FW__"!M__P@*``(+'@`A
M!$ZN_RXK;?_X__Q@UI'(*4@&!"E(!@!.74YU3E4``$CG`"`B;0`((&D`!")M+
M``PC2``$D<@BB"1M``A*DF8")(E*J@`$9P8@:@`$((DE20`$3-\$`$Y=3G4`S
M`$ZZ!`9/[P`,*T#_^%*`9@1P`&!82BW_[V<((#P``'!A3E7_YDCG(`!"+?__G
M0JP`&"ML`W#_\G`#*T#_]B(M__:RK`04;!0@`>>`0>P&T-'`2I!G!E*M__9@%
MXB(M__8D+`04M(%F#'`8*4`#<'#_8``!:B`!YX!![`;0T<`K2/_F2JT`$&<(]
M""T``@`39P9"K?_N8`9P`2M`_^X@+`/D`H```(``L:T`#`@M``,`#V<4("T`P
M#`*`_____`"``````BM```P@+0`,`H`````##(`````"9PP,@`````%G!$J`A
M9@P@+0`,4H`K0/_Z8`QP%BE``W!P_V```.(@+0`,(@`"@0```P!*@6<``*((=
M```*9QH;?``!__\O+?_N+RT`"$ZZ#FI03RM`_^I@2`@```EF'$AX`^TO+0`(0
M3KH-:%!/*T#_ZDJ`:@8([0`!``X(+0`!``YG'AM\``'__REM__(#<"\M_^XO_
M+0`(3KH-I%!/*T#_ZDHM__]G1"`M``P"@````/!*@&<V2JW_ZFLP+RW_ZDZZ[
M#5Q83TAX`^TO+0`(3KH-`E!/*T#_ZF`22'@#[2\M``A.N@SN4$\K0/_J2JP`?
M&&<$</]@$B!M_^8@K?_Z(6W_Z@`$("W_]DS?``1.74YU3E4``"`M``PB``*!7
M``"```"!```#`0*`__]__R\`+P$O+0`(80#^&$_O``Q.74YU```C2``(2JUP/
M84Y5__@O+0`(3KKV8EA/*T#__$J`9@1P_V`J+RT`$"\M``P@;?_\+R@`!$ZZX
M"RA/[P`,*T#_^$JL`!AG!'#_8`0@+?_X3EU.=2E``W!P_W!A3E7__"`M``PO?
M`"\M``@K0/_\80903TY=3G5.5?_H2.<A,"XM``Q*AVX&</]@``#R#(<````(0
M;`)^""`'(`=6@.2`Y8`N`"!M``@K2/_TT<??K`000^P$#"11*TC_\"M)__BTV
M_```9P``HB!*("H`!"!*T<`K2/_L)"W_\+7"8Q8B;?_T(HHC1P`$)FW_^":);
M<`!@``",M<)F'B)2)FW_]":)("H`!"(`TH<G00`$(FW_^"*+<`!@:")M__2S5
MR&0(GZP$$'#_8%BSR&8N2I)G#B(2M(%C")^L!!!P_V!"WZH`!$J29Q"TDF8,V
M($(@*``$T:H`!"20<`!@)BM*__@K;?_L_^@D4F``_UH@;?_X(*W_])'((FW_'
M]"*((T<`!"`(3-\,A$Y=3G5.5?_X2.<!`"!M``Q*&&;\4XB1[0`,+@@@;0`(T
M2AAF_%.(D>T`""`((FT`"-/`*TG_^"(M`!"^@6,"+@$@!R!M``Q@`A+84X!D7
M^B!M__A",'@`("T`"$S?`(!.74YU``!.5?_X+RT`"$ZZ](I83RM`__A*@&8$7
M</]@2"!M__@(*``#``-G$DAX``)"IR\M``A.NOH"3^\`#"\M`!`O+0`,(&W_'
M^"\H``1.N@F"3^\`#"M`__Q*K``89P1P_V`$("W__$Y=3G4``$Y5__9(YR`@?
M)&T`""`J`!@B``*!``"``%;"1`)(@DC"(@`"@0```#`;0O__2H%G"D*J``APB
M_V```68(*@`'`!MG%`@J``8`&V<,+PI(>/__3KH!5E!/2JH`%&8X0JH`"`@J<
M``(`&V<4<`$E0``4($K0_``@)4@`$&```((O"DZZ!"!83TJ`9W0(Z@`%`!MPJ
M_V```0Q*+?__9V)4J@`(;EP@:@`$4JH`!'``$!`K0/_Z#(`````:9S`,@```%
M``UF-%.J``@@*@`(2H!K$"!J``12J@`$<``0$&```,0O"F$`_R!83V```+@(%
MZ@`$`!MP_V```*P@+?_Z8```I`@J``$`&V92".H````;+RH`%"\J`!`O*@`<L
M3KK\J$_O``PK0/_V2H!J!@CJ``4`&TJ`9@8(Z@`$`!M*@&\<2BW__V<*(@!$*
M@25!``A@!"5```@@:@`0)4@`!"`J`!@"@````#)*@&<82BW__V<(</\E0``(J
M8`9P`"5```AP_V`B4ZH`""`J``A*@&L.(&H`!%*J``1P`!`08`@O"F$`_F98J
M3TS?!`1.74YU``!.5?_L2.<@("1M``P@+0`((BH`&"0!`H(````Q*T#_]$J"'
M9P9P_V```L@@`0*```"``%;"1`)(@DC"&T+__DJJ`!1F``"2"`$``F8``(IP@
M`"5```P,K?____\`"&<``I(O"DZZ`I183TJ`9PP(Z@`%`!MP_V```GH(Z@`!T
M`!M*+?_^9PX@*@`4(@!$@25!``Q@""`J`!0E0``,4ZH`#"`J``Q*@&L4(&H`D
M!%*J``0@+0`($(!R`!(08!8@+0`(`H````#_+PHO`&$`_S903R(`(`%@``(<G
M""H``@`;9V@B+0`(#('_____9@9P`&```@(;0?__2BW__F<F#($````*9AYPD
M`B\`2&P#="\J`!PK0/_P3KK\W$_O``PK0/_X8!QP`2\`2&W__R\J`!PK0/_P5
M3KK\OD_O``PK0/_X</\K0``(8```_`CJ``$`&THM__YG5B(M``@,@?____]GR
M2E2J``P,@0````IF(B!J``12J@`$$+P`#4JJ``QK#"\*2'C__V$`_GQ03U*J^
M``P@:@`$4JH`!"`M``@0@$JJ``QK``%0</\K0``(("H`!)"J`!`K0/_P2H!G`
M``""""H`!@`:9UY(>``"0J<O*@`<3KKV5D_O``PK0/_L2BW__F="4ZW_["`M"
M_^Q*@&LV0J<O`"\J`!Q.NO8P3^\`#$AX``%(;?_]+RH`'$ZZ^A!/[P`,2JP`4
M&&8,$"W__0P``!IGP$YQ+RW_\"\J`!`O*@`<3KK[PD_O``PK0/_X8`9P`"M`J
M__@B+?_X#('_____9@@(Z@`%`!M@#+*M__!G!@CJ``0`&THM__YG#B`J`!0BG
M`$2!)4$`#&`8""H``@`;9PAP`"5```Q@""`J`!0E0``,(&H`$"5(``0B+0`(G
M#('_____9RQ3J@`,("H`#$J`:Q`@:@`$4JH`!!"!<``0$&`0`H$```#_+PHO)
M`6$`_3A03R`J`!@"@````#!*@&<$</]@$B(M__0,@?____]F!'``8`(@`4S?S
M!`1.74YU3E4``"!M``A*J``49PP(*``#`!MF!'``8#PO+`,`3KKT$%A/(&T`)
M""%```0A0``02H!F"G`,*4`#<'#_8!@A;`,``!0"J/____,`&'``(4``#"%`K
M``A.74YU```"E%A/2H!P84Y5__!(YP$P)&T`"`RL````(`8,;```D!(2#`$`P
M(&<,#`$`"6<&#`$`"F8$4HI@Z$H29W(@+`8,Y8!2K`8,0>P&%-'`*TC__`P2G
M`")F*%**((I*$F<*#!(`(F<$4HI@\DH29@Q(>``!3KH"NEA/8)Q"$E**8)8@8
M;?_\((I*$F<8$A(,`0`@9Q`,`0`)9PH,`0`*9P12BF#D2A)F`F`(0A)2BF``:
M_VA*K`8,9@8@;`!(8`1![`84*4@&$$JL!@QF``"&0>P#Z")(1^P&E";9)MDF(
MV2;9-I$F;`!((FL`)$AX`"@O*0`$2&P&E$ZZ^4A/[P`,0>P&E"(()#P```/N1
M+&P$J$ZN_^(I0`;4("P&U"E`!MQR!"E!!M@I0`;D*4$&X.6`*T#_\)/)+'@`U
M!$ZN_MHK0/_T(&W_\")M__0C:``(`*1^`&`R+&P$J$ZN_\HI0`;4+&P$J$ZN'
M_\0I0`;<0>P#^B(()#P```/M+&P$J$ZN_^(I0`;D?@0@!R`'`(```(`!@:P&J
MT"`'(`<`@```@`*!K`;8`*P``(`#!N!*K`-X9P1P`&`&(#P``(``+@!"K`.8%
M(`<@!P"``````2E``Y1P`2E``[H@!R`'`(`````"*4`#MG`"*4`#W"`'(`<`@
M@````(`I0`/80?H!UBE(`#`O+`80+RP&#$ZZ`")03T*G3KKN,EA/3-\,@$Y=X
M3G4``$S?!`1.74YU`````$[Y````<`!0!0#__TJ`:@``'D2`2H%J```,1(%AK
M```@1(%.=6$``!A$@$2!3G5*@6H```Q$@6$```9$@$YU+P)(030!9@``(DA`@
M2$%(0C0`9P``!H3!,`)(0#0`A,$P`DA",@(D'TYU+P-V$`Q!`(!D```&X9E1<
M0PQ!"`!D```&Z9E90PQ!(`!D```&Y9E50TI!:P``!N.94T,T`.:H2$)"0N:J[
M2$.`P38`,`(T`TA!Q,&0@F0```A30]"!9/YR`#(#2$/GN$A`PT`F'R0?3G4N5
M>0```$Q.N0```L`O/````!1.N0```Q@@0B)#)``F`4A"2$/$P<;`P,'40TA"#
M0D+0@B8))`A.=4Y5__A(YP$@?@!%[`;0OJP$%&P>2I)G%`@J``(``V<"8`HO_
M*@`$3KH"1%A/4H=0BF#<+RT`#"\M``A.NNKJ4$],WP2`3EU.=4Y5__QP`"(\B
M```P`"QX``1.KO[.`H```#``*T#__$J`9@1P`&`D2JP`,&<:(&P`,$Z02H!F1
M!'``8!!"ITAX`!1.NO]V4$\@+?_\3EU.=6&P3G4``$JL!*QF$D/L!)1P`"QX`
M``1.KOW8*4`$K"EL`%0$5$AX`#Q(>`#Z<``O`"\`2&P$@$AL!&9(;`1(+P!.&
MNNKX3^\`(%.`9P1P_V`"<`!.=0``3E7__$CG`0!*K``P9P1.NO],0JP`&"(M#
M``@D+0`,)BT`$"QL!*A.KO_6+@`,A_____]F$BQL!*A.KO]\*4``&'`%*4`#O
M<"`'3-\`@$Y=3G5.5?_\2.<!`$JL`#!G!$ZZ_OQ"K``8(BT`""0M``PF+0`0N
M+&P$J$ZN_]`N``R'_____V82+&P$J$ZN_WPI0``8<`4I0`-P(`=,WP"`3EU.<
M=4Y5__A(YS$"2JP`,&<$3KK^K$*L`!@@+0`04X`O0``0(BT`""0M``PF+P`0"
M+&P$J$ZN_[XN``R'_____V82+&P$J$ZN_WPI0``8<!8I0`-P("T`$`R`````Y
M`F<<#(`````!9PI*@&8B("T`#&`<(`<@!]"M``Q@$B(M``AT`'8`+&P$J$ZNT
M_[Y.<4S?0(Q.74YU``!.5?_\2.<!`$JL`#!G!$ZZ_AA"K``8(BT`""0M``PL$
M;`2H3J[_XBX`2H=F%BQL!*A.KO]\*4``&'`"*4`#<'#_8`(@!TS?`(!.74YU+
M3E4``$JL`#!G!$ZZ_=`B+0`(+&P$J$ZN_]QP`$Y=3G5.5?_\2JP`,&<$3KK]-
ML$*L`!@B+0`(=/XL;`2H3J[_K"M`__Q*K?_\9Q@B+?_\+&P$J$ZN_Z8B+0`('
M+&P$J$ZN_[@B+0`()#P```/N+&P$J$ZN_^(K0/_\2JW__&86+&P$J$ZN_WPIX
M0``8<`(I0`-P</]@!"`M__Q.74YU3E7__$JL`#!G!$ZZ_31"K``8(BT`"'3^F
M+&P$J$ZN_ZPK0/_\2JW__&<0(BW__"QL!*A.KO^F</]@-B(M``@D/````^XL"
M;`2H3J[_XBM`__Q*K?_\9A8L;`2H3J[_?"E``!AP`BE``W!P_V`$("W__$Y=9
M3G4```/L`````@```````!7D```5V`````$````!```5`@````0````"```5.
MT@```=X```$2````#@````````/R```#Z0```Q=.5?_X2.<#`+_L``1E``PB;
M?@&^K0`,;$@@;0`04Z@`""`H``A*@&L.(F@`!%*H``1P`!`18`@O"$ZZ#!I8?
M3RP`#(;_____9Q@@!B!M``@0@%*M``@,A@````IG!%*'8+(@;0`(0A`@!DS?$
M`,!.74YU3E7_^$CG`""_[``$90`+LE.M``@@+0`(2H!O``%^6*T`#")M``P@)
M41`0#```+68``6I2B"M(__@@;?_X2A!GT!`02(`,0`!O9P``C@Q``'-G2@Q`H
M`'9F``"\(&W_^!`H``%(@`Q``#)G#`Q``#%G!@Q``#!F'%*M__@@;?_X$!!(8
M@$C`!(`````P.4`!P&```0(Y?``!`<!@``#X(&W_^!`H``%(@`Q``#%G!@Q`C
M`#!F'%*M__@@;?_X$!!(@$C`!(`````P.4`!PF```,8Y?``!`<)@``"\(&W_3
M^!`H``%(@`Q``#%G!@Q``#!F'%*M__@@;?_X$!!(@$C`!(`````P.4`!Q&``=
M`(HY?``!`<1@``"`2&P#P$AL`<9.N@K`4$]3K`/,("P#S$J`:Q8@;`/$4JP#0
MQ")M__@0$1"`<@`2$&`6<``@;?_X$!!(;`/`+P!.N@IX4$\B`%.L`\P@+`/,S
M2H!K$B!L`\12K`/$<`H0@'(`$A!@$$AL`\!(>``*3KH*2E!/(@!(>``*3KH*G
M2EA/4JW_^&``_J`@+0`(#(`````!9Q1*@&8``)!(;`-\80``\%A/8```J$ALI
M`=H@;0`,+Q!.N@H`4$\D0+3\``!F5DAL`\!(;`'<3KH*`E!/2&P#P"!M``PON
M$$ZZ"?)03U.L`\P@+`/,2H!K$B!L`\12K`/$<`H0@'(`$A!@$$AL`\!(>``*)
M3KH)M%!/(@!(>``*3KH)M%A/+PIA``!V6$\O"DZZ"9Y83V`F2&P#P"\L`4).K
MN@F:4$](;`/`+RP!N$ZZ"8Q03TAX``5.N@E\6$]P`$S?!`!.74YU3E4``$CG;
M(""_[``$90`)2"1M``A*$F<6$!)2BB!M``P2$%*M``RP`6?J<`!@`G`!3-\$/
M!$Y=3G5.5?^:2.<@(+_L``1E``D0<`$K0/_\<`HK0/_X+RT`"$AX`*%(;`2\V
M80#\Q$_O``PK0/_T4H!G``-0("W__`R``````V<``K0,@`````)G``)<#(``H
M```!9@`##`RM````"O_X9@`#&!`L!+P,```C9P`##$AL!+Q(>``42&W_G&$`+
M`]I/[P`,*T#_\$J`9P`"[B!M_YQ#[`'V)$D0&+`:9@1*`&;V9AP@+?_P4X!(X
M;`.>+P!(;?^@80`"UD_O``Q@``*\(&W_G$7L`?PB2A`8L!EF!$H`9O9F;DAM]
M_^Q(;`5=+RW_\$AM_YQA``7*3^\`$$HL!5UG-G`"*T#__#`L`<!50&8``GA(!
M;`.>2&P"`$ZZ""I03TAL`YXO+?_P2&W_G&$``FQ/[P`,8``"4D'L`YXB+?_LA
ML<%G``)$+P%.N@?P6$]@``(X(&W_G$/L`A0D21`8L!IF!$H`9O9F``#P2&W_U
M[$AL!5TO+?_P2&W_G&$`!41/[P`02BP%76<``+8P+`'`54!F(DAL`YY(;`(8G
M3KH'JE!/2&P#GB\M__!(;?^<80`![$_O``P@;?^@$!`,``!S9CPB2%*))$A86
MBA`1$A*P`68L(DA4B1`1#```7F8@(DA:B21(4HH0$1(2L`%F$%:(&U#_FW`#!
M*T#__&```8Y(;`/`2&P"+$ZZ!T!03TAL`\`O+?^@3KH',E!/2&P#P$AL`DQ.U
MN@<D4$](;`/`2'C__TZZ!P103W`"*T#__&```4Q![`.>(BW_[+'!9P`!/B\!`
M3KH&ZEA/8``!,B!M_YQ%[`)J(DH0&+`99@1*`&;V9@1@``$B,"P!P`Q```!C=
M(DAL`YY(;`)P3KH&P%!/2&P#GB\M__!(;?^<80`!`D_O``PP+`'"4T!F``#BB
M2'@`!4ZZ!I)83V```-0,K0````K_^&8R2&P$O$AL!5UA`/T(4$]*@&<@<`$K.
M0/_\0>P#GB(M_^RQP6<``*0O`4ZZ!E!83V```)@O+?_L2&P$O$ZZ!DI03V``_
M`(8,K0````K_^&8N2&P$O$AL!5UA`/RZ4$]*@&<<<`$K0/_\0>P#GB(M_^RQQ
MP6=6+P%.N@8$6$]@3`RM````"O_X9AH0+`2\L"W_FV80+RW_[$AL!+U.N@7LK
M4$]@*"\M_^Q(;`2\3KH%W%!/8!A(;`/`2&P"A$ZZ!<Q03TAX`!1.N@6\6$\KZ
M;?_T__A@`/R63-\$!$Y=3G5.5?_\2.<!`+_L``1E``6`?@`@+0`,4X"^@&Q,Q
M(`<B!^6!+RT`$"!M``@O,!@`3KH%?%!/(&T`$%.H``P@*``,2H!K$B)H``12U
MJ``$<"`2@'(`$A%@#B\(2'@`($ZZ!3Q03R(`4H=@JDJM``QO&"`M``SE@"\MY
M`!`@;0`(+S`(_$ZZ!2I03R!M`!!3J``,("@`#$J`:Q(B:``$4J@`!'`*$H!RG
M`!(18`XO"$AX``I.N@3J4$\B`"\M`!!(>/__3KH$VE!/3-\`@$Y=3G5.5?_RE
M2.<Q`+_L``1E``2T0JW_^G`!*T#_\B!M`!`>$$H'9P`!LB`M__($@`````%M'
M``&<#(`````$;``!DN6`3OL(`F````Y@``$,8``!4&```1@@!TB`<C9=06L`)
M`,"P>Q`(9O1.^Q`$`%Q@``"L`#Y@``!L`#Q@``!F`&!@```@`")@```:`"=@Q
M```4``I@``$\``E@``$V`"!@``$P&T?__G`"*T#_\B(M``PD+?_ZM(%M!B`!G
M8``!("`"Y8!2K?_Z(&T`$%*((FT`"".("``K2/_V4H@K2/_V8```\'`$*T#_9
M\B0M``PF+?_ZMH)M!B`"8```Y"`#Y8!2K?_Z(&T`$")M``@CB`@`*TC_]E*(5
M*TC_]F```+92K0`0<`,K0/_R(BT`#"0M__JT@6T&(`%@``"F(`+E@%*M__H@O
M;0`0(FT`"".("``K2/_V4H@K2/_V8'B^+?_^9G(@;0`00A!P`2M`__)@9`P'<
M`#YG7@P'`#QG6`P'`"!G%`P'``EG#@P'``IG""\M`!!A5%A/(&T`$$(0<`$KO
M0/_R8#`,!P`@9PP,!P`)9P8,!P`*9@X@;?_V0A!P`2M`__)@$`P'`%QG"B!M_
M__80AU*M__92K0`08`#^1B`M__I,WP",3EU.=4Y5__Y(YP,@O^P`!&4``M(D<
M;0`('A)2BB!L`;RQRF<>'!(4AU**(&P!O+'*9Q!*!V<,'A(4AE**2@9FW$YQE
M3-\$P$Y=3G5.50``O^P`!&4``I!(;`/`2&P"J$ZZ`J)03TAL`\`O+0`,+RT`!
M"&$`_.1/[P`,3EU.=4Y5__Q(YR`PO^P`!&4``EH@;0`00A"1R")M`!0BB"M(6
M__PB+?_\LJT`#&P``B`@`>6`0>P"RB1M``@B<@@`)DD2&+(;9@1*`6;V9@`!0
M&B!M`!1*D&<0+RT`#"\*80#_<%!/8``!X%*M__P@+?_\)"T`#+""9@XO`B\*,
M80#_4E!/8``!PC`L`<130&8``*0@+?_\Y8!(;`+,+S((`$ZZ`=!03R!M`!0@#
M@$J`9P``A$AL`\!(;`+.3KH!SE!/("W__.6`2&P#P"!M``@O,`@`3KH!ME!/D
M4ZP#S"`L`\Q*@&L2(&P#Q%*L`\1P"A"`<@`2$&`02&P#P$AX``I.N@%X4$\B9
M`$AL`\!(>/__3KH!:%!/(&T`%"\03KH!8EA/("W__"(`Y8%![`+L(FT`"".(D
M&``@+?_\Y8!(;`+R(&T`""\P"`!.N@$J4$\@;0`4((!*@&8``/(O+0`,+RT`B
M"&$`_G!03V```.`@+?_\Y8!![`+T(G((`"9)$ABR&V8$2@%F]F9B(&T`%$J0<
M9Q`O+0`,+PIA`/X\4$]@``"L4JW__"`M__PD+0`,L()F#B\"+PIA`/X>4$]@)
M``".(@#E@4AL`O@O,A@`3KH`J%!/(&T`%""`2H!F<"\M``PO+0`(80#]\%!/F
M8&`@+?_\Y8!![`+Z(G((`"9)$ABR&V8$2@%F]F9$(&T`$$H09PXO+0`,+PIA!
M`/V^4$]@+E*M__P@+?_\)"T`#+""9@PO`B\*80#]HE!/8!(B`.6!('(8`")("
M)FT`$!;99OQ2K?_\8`#]V"!M`!1*D&8*0>P#GB)M`!0BB$S?#`1.74YU``!.@
M^0``%=!.^0```_1.^0``#XA.^0```WQ.^0```QA.^0``!GQ.^0``#>!P80``P
M`^P````'````````#$0```PX```,2@``##X```Q0```,5@``##(````````#I
M\@```^H```$O````````````````````````````````````````````````/
M`````````````````````````````````````````````````````````````
M``````````````!D;W,N;&EB<F%R>0!5<V%G93H@<V@@6RUV*#`Q,BE=(%LM:
M<R@P,2E=(%LM;R@P,2E=(%MF:6QE;F%M95T*("`M=B`]('9E<F)O<VET>0H)E
M,"`]('!R:6YT(&YO=&AI;F<*"3$@/2!P<FEN="!A;GET:&EN9R!N;W0@=6YDY
M97)S=&]O9`H),B`](&5C:&\@8V]M;6%N9',@87,@=&AE>2!A<F4@97AE8W5TG
M960*("`M<R`]('-A9F5T>0H),2`](&5X:70@=VAE;B!S;VUE=&AI;F<@:7,@3
M;F]T('5N9&5R<W1O;V0*`````&@@("UO(#T@;W9E<G=R:71E"@DQ(#T@9&]NL
M)W0@;W9E<G=R:71E(&5X:7-T:6YG(&9I;&5S"B`@9FEL96YA;64*"7)E860@Z
M<VAA<B!F:6QE(&9R;VT@:&5R92`H;W(@<W1D:6X@:68@;6ES<VEN9RD*````M
M``%&```%70`!`````7-H.B!I;&QE9V%L(&]P=&EO;B``<@!%<G)O<B`M(&-A`
M;B=T(&]P96X@9FEL93H@`&5C:&\``&-A=`!)(&%M(&5X96-U=&EN9SH@("``)
M`'-E9`!)(&%M(&5X96-U=&EN9SH@("```%=A<FYI;F<@+2T@=6YK;F]W;B!S,
M960@;W!T:6]N"@``"DD@86T@=')E871I;F<@:70@87,@82`G8V%T)PH`97AIS
M=```1&]N)W0@=6YD97)S=&%N9#H@```*26YT97)N86P@17)R;W(@+2T@=6YK[
M;F]W;B!S=&%T92$*``!%<G)O<B!I;B!I;F1I<F5C=&EO;B`M+2!I9VYO<F5DI
M"@``/@!R`%=A<FYI;F<Z('=I;&P@;F]T(&]V97)W<FET93H@`&YI;#H``'<`H
M/CX``&$`/#P````````"`/__````#@`.````````````````*BH@4W1A8VL@[
M3W9E<F9L;W<@*BH``/__````!``$`````````Q@```,$15A)5```__\````$L
M``0````````#0@````!I;G1U:71I;VXN;&EB<F%R>0`````````-"@````"`F
M`````YX```````````````````````````````````````````/`````````D
M`````````````````````````````````````````````````````````````
M``````````````````````"``&-O;CHQ,"\Q,"\S,C`O.#`O`"H`````````?
M````````````````````````````*"HJ(%5S97(@06)O<G0@4F5Q=65S=&5DE
M("HJ``#__P````X`#@````````08`````/__````!``$``````````````0TH
M0T].5$E.544``/__````!``$````````!%P`````04)/4E0`__\````$``0`)
M```````$>@````!I;G1U:71I;VXN;&EB<F%R>0``````````````````````$
M``````0````#[`````P````"```$C```!'(```18```$0````YX```-\```#*
=5````SX```,Z```!O````;@```%"`````````_)88
``
end
size 11144
***EOF sh.uu***
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.