[comp.sources.amiga] v89i017: fts - font lister

page@swan.ulowell.edu (Bob Page) (02/03/89)

Submitted-by: att!cuuxb!migh (Mike Hall)
Posting-number: Volume 89, Issue 17
Archive-name: fonts/fts.1

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	Fts.Doc
#	Compile.It
#	fts.c
#	fts.uu
# This archive created: Mon Jan 30 19:09:32 1989
cat << \SHAR_EOF > Fts.Doc


		FTS: list fonts and their attributes

`fts' lists information that the system gives it about the fonts located 
in "FONTS:".  The listing is sorted by font names and sizes.  The output 
has 5 columns, like this:

	Name  Size  Type  Style  Flags

The `Name' column shows the font name.

The `Size' column prints in decimal the font size given by the value
in the ta_YSize field in the TextAttr structure.  It's a measure of the 
height of the font in pixels.

The `Type' column shows where the font is located, and has two fields, "dm",
shown below.  Its information comes from af_Type in the AvailFont structure:

	d	disk		AFF_DISK
	m	memory		AFF_MEMORY

The `Style' column has four fields, "ubie", shown below.  Its information
comes from ta_Style in the TextAttr structure:

	e	extended	FSF_EXTENDED
	i	italic		FSF_ITALIC
	b	bold		FSF_BOLD
	u	underline	FSF_UNDERLINED
	
Finally, the `Flags' column has 8 fields, "xspwtbdr", which come from the
ta_Flags field in the TextAttr structure:

	x	removed		FPF_REMOVED
	s	designed	FPF_DESIGNED
	p	proportional	FPF_PROPORTIONAL
	w	wide dot	FPF_WIDEDOT
	t	tall dot	FPF_TALLDOT
	b	backwards	FPF_REVPATH
	d	disk font	FPF_DISKFONT
	r	rom font	FPF_ROMFONT

To show how to invoke `fts', here is the usage information from `fts':
	1> fts -?
	Use:  fts  -t[dm] -s[eibu] -f[xspwtbdr] -y# -n(name) -S
		Type:	-t	d = disk		m = memory
		Style:	-s	e = extended		i = italics
				b = bold		u = underline
		Flags:	-f	x = removed		s = designed
				p = proportional	w = wide dot
				t = tall dot		b = backwards
				d = disk font		r = rom
		Size:	-y#	where `#' is YSize of the font
		Name:	-n...	where `...' is the font name
		Sort:	-S = sort by size instead of name

Calling `fts' with no arguments will print all the fonts the system
finds under "FONTS:".  At the end of the list, it reports the number
of selected fonts and the total number of fonts on the system.

The listing is sorted by name, and fonts with the same name are sorted
by size.  To sort the listing by size instead of name, use the -S switch.
When sorting by size, fonts of the same size are sorted by name.

The decision to print a font or not works like this:
	- if the size doesn't match, the font isn't printed
	- if the name doesn't match, the font isn't printed
	- if the font attributes matches ANY of the requested attributes,
	  then font is printed
Note that "-tdm" does NOT give fonts that are both in memory and on disk,
it gives all fonts that are either in memory or on disk (i.e. all fonts).

Here is a shortened sample output:
	1> fts
	Font Name	      Size Type Style  Flags
	clean                   8   dm  ----  -s----dr
	 ...
	Helvetica               9   d-  ----  -sp---d-
	Helvetica              11   d-  ----  -sp---d-
	Helvetica              13   d-  ----  -sp---d-
	Helvetica              15   d-  ----  -sp---d-
	Helvetica              18   d-  ----  -sp---d-
	Helvetica              24   d-  ----  -sp---d-
	 ...
	topaz                   8   -m  ----  -s-----r
	topaz                   9   -m  e---  -s--t--r
	65 / 66 fonts.

Note that `fts' will combine a disk-based and memory-based font on the
same line, if all other attributes are the same (e.g. the "clean" font).
Because of this, it says that 65 fonts were listed, although there are
66 available.

Also, `fts' shows the ROM based fonts, "topaz 8" and "topaz 9" (the Type
is `m' which means in-memory, and the rom-font Flag is set.)

Here's another example, in which we sort by size, and ask for the fonts
whose name begins with `cmr':
	1> fts -ncmr -S 
	Font Name	      Size Type Style  Flags
	cmr6                   26   d-  ----  -sp---d-
	cmr8                   34   d-  ----  -sp---d-
	cmr10                  42   d-  ----  -sp---d-
	cmr12                  50   d-  ----  -sp---d-
	cmr14                  59   d-  ----  -sp---d-
	5 / 66 fonts.

And a case where we ask for the in-memory fonts:
	1> fts -tm
	Font Name	      Size Type Style  Flags
	clean                   8   dm  ----  -s----dr
	topaz                   8   -m  ----  -s-----r
	topaz                   9   -m  e---  -s--t--r
	3 / 66 fonts.

And another case where we ask for the "tall-dot" fonts, sorted by size:
	1> fts -ft -S
	Font Name	      Size Type Style  Flags
	topaz                   9   -m  e---  -s--t--r
	boarder                13   d-  e---  -s--t-dr
	2 / 66 fonts.

Some future things to consider:
	- performance: sort after select (?)
	- collect on same line the same font with different sizes (how?)
	- specify alternate "FONT:" path (via 'assign's?)
	- specify and/or combinations of arguments (is this needed?)
	- specify keyword arguments (i.e. "style bold") (like CLI commands)
	- reallocate avail-font structure if it's not big enough

There is a compile-time option, FTS_LARGE, which adds in the selection,
help, and sort features.  If you want a smaller version, just compile it
plain; if you want all the options, compile with "-DFTS_LARGE".
These are the sizes I get, compiling with Lattice 5.0:
	fts.lrg          9068 ----rwed Today     20:58:39
	fts.sm           7640 ----rwed Today     20:56:07

The buffer that I give the system to place the font information in
is 2000 bytes long.  I believe that it should hold information for
199 fonts, but I haven't tested this.  If you get the message "Some
fonts not gotten...", then increase the parameter AFSIZE from 2000L
to ((10 * number-of-fonts) + 2).

I haven't checked on the validity of all the flag/style/type bits,
but they look reasonable to me.  There are no bugs that I know of,
but please let me know if something goes wrong, or if it just
doesn't seem to work right!

Mike Hall			att!cuuxb!migh
2 S 461 Cherice Dr.		312-393-6350
Warrenville, IL 60555

SHAR_EOF
cat << \SHAR_EOF > Compile.It
lc -Lcdn -w -r -b fts
SHAR_EOF
cat << \SHAR_EOF > fts.c
/*
 *	list fonts, print info about them
 */

/* Two versions:  
 *	large, with select/sort/help options	-DFTS_LARGE
 *	small, bare-bones			 (default)
 */

#include	<proto/exec.h>
#include	<proto/diskfont.h>

#include	<string.h>
#include	<exec/types.h>
#include	<exec/memory.h>
#include	<libraries/diskfont.h>
#include	<graphics/text.h>

#define	AFSIZE	2000L

long	brkclean();
short	fontcmpname();
void	mkflags(), combine(), fixname(), cleanup(), shortbits();

#ifdef FTS_LARGE
void	showhelp(), doargs();
short	fontcmpsize(), select(), mksel();
#endif

/* if you don't use 'proto/diskfont.h', then you'll need this:
extern long DiskfontBase;
 */

struct	AvailFontsHeader	*afh;

#ifdef FTS_LARGE
struct	selinfo {		/* place to keep select options */
	short	valid, type, style, flags, ysize, sort;
	char	*name;
};
/* flag bits for 'valid' selections (things that the user set) */
#define	SEL_TYPE	0x01
#define	SEL_STYLE	0x02
#define	SEL_FLAGS	0x04
#define	SEL_YSIZE	0x08
#define	SEL_NAME	0x10
#define	SEL_SORT	0x20
#endif

#define	FBUFSZ	24		/* length of flags string in the listing */

void main(ac, av)
int	ac;
char	*av[];
{
	register struct	AvailFonts		*af;
	register struct	TextAttr		*ta;
	unsigned short	num ,i, cnt;
	char	flagbuf[FBUFSZ];
#ifdef FTS_LARGE
	struct	selinfo	selinfo;
#endif

	afh = NULL;			/* init for cleanup */
	onbreak(&brkclean);		/* clean-up on ^C/^D */

#ifdef FTS_LARGE
	selinfo.sort = 0;		/* sort by name */
	selinfo.valid = 0;		/* no others have been selected */
	selinfo.type = selinfo.style = selinfo.flags = 0;
#endif
	afh = (struct AvailFontsHeader *) AllocMem(AFSIZE, MEMF_CLEAR);
	if ( afh == NULL ) {
		exit(10L);
	}
	if ( (DiskfontBase = OpenLibrary("diskfont.library",0L)) == (long)NULL ) {
		cleanup();
		exit(15L);
	}
#ifdef FTS_LARGE
	doargs(ac, av, &selinfo);	/* find out what they asked for */
#endif
	if ( AvailFonts((char *)afh, AFSIZE, 0xffL) != 0 )
		printf("Some fonts not gotten...\n"); /* really should try again */
	num = afh->afh_NumEntries;
	af = (struct AvailFonts *) &afh[1];		/* trick from RKM */

	qsort(af, num, sizeof (struct AvailFonts),	/* sort names/sizes */
#ifdef FTS_LARGE
		 (selinfo.sort == 0) ? fontcmpname : fontcmpsize );
#else
		 fontcmpname);
#endif
	printf("Font Name	      Size Type Style  Flags\n");
	cnt = 0;
	for ( i = 0; i < num; i++, af++ ) {
		ta = &af->af_Attr;
		if ( ta->ta_Name == NULL ) {	/* when "combined" */
			continue;
		}
		if ( i < (num-1) ) {		/* see if we can combine */
			combine(af);
		}
		fixname(ta->ta_Name);		/* get rid of ".font" */
#ifdef FTS_LARGE
		if ( !select(af, &selinfo) ) {	/* if no match, skip it */
			continue;
		}
#endif
		mkflags(flagbuf, af->af_Type, ta->ta_Style, ta->ta_Flags);
		printf("%-20s  %3d  %s\n", ta->ta_Name, ta->ta_YSize, flagbuf);
		cnt++;
	}
	printf("%d / %d fonts.\n", cnt, num);
	cleanup();
	exit(0L);
}

void cleanup()
{
	if ( DiskfontBase != NULL )
		CloseLibrary(DiskfontBase);
	if ( afh != NULL ) 
		FreeMem((char *)afh, AFSIZE);
}

long brkclean()
{
	cleanup();
	return 1L;
}

#ifdef FTS_LARGE
/*
 *	read command line args, fill in selinfo struct
 */
void doargs(ac, av, sp)
register int	ac;
register char	**av;
register struct selinfo *sp;
{
	register char *p;

	while ( --ac > 0 ) {
		p = *++av;
		if ( *p != '-' )
			continue;
		++p;
		switch ( (*p++) ) {
		case 't':		/* set type */
			sp->type |= mksel(p, "dm", 0x2);
			sp->valid |= SEL_TYPE;
			break;
		case 's':		/* set style */
			sp->style |= mksel(p, "eibu", 0x8);
			sp->valid |= SEL_STYLE;
			break;
		case 'f':		/* set flags */
			sp->flags |= mksel(p, "xspwtbdr", 0x80);
			sp->valid |= SEL_FLAGS;
			break;
		case 'y':		/* set ysize (only one!) */
			sp->ysize = atoi(p);
			sp->valid |= SEL_YSIZE;
			break;
		case 'n':		/* set name (only one!) */
			sp->name = p;
			sp->valid |= SEL_NAME;
			break;
		case 'S':		/* set sort by Size */
			sp->sort = 1;
			sp->valid |= SEL_SORT;
			break;
		default:
			showhelp();
		}
	}
}
#endif

#ifdef FTS_LARGE
/*
 *	print help message, and cleanup and exit 
 */
void showhelp()
{
printf("Use:  fts  -t[dm] -s[eibu] -f[xspwtbdr] -y# -n(name) -S\n");
printf("	Type:	-t	d = disk		m = memory\n");
printf("	Style:	-s	e = extended		i = italics\n");
printf("			b = bold		u = underline\n");
printf("	Flags:	-f	x = removed		s = designed\n");
printf("			p = proportional	w = wide dot\n");
printf("			t = tall dot		b = backwards\n");
printf("			d = disk font		r = rom\n");
printf("	Size:	-y#	where `#' is YSize of the font\n");
printf("	Name:	-n...	where `...' is the font name\n");
printf("	Sort:	-S = sort by size instead of name\n");
	cleanup();	/* clean up and exit */
	exit(0);
}
#endif

/*
 *	compare two AvailFonts structs, to sort by name/ysize
 */
short fontcmpname(f1, f2)
struct AvailFonts *f1, *f2;
{
	int	stricmp();
	short	r;
	register struct	TextAttr *t1, *t2;

	t1 = &f1->af_Attr;
	t2 = &f2->af_Attr;
	if ( (r = stricmp(t1->ta_Name, t2->ta_Name)) == 0 ) {
		r = t1->ta_YSize - t2->ta_YSize;
	}
	return r;
}

#ifdef FTS_LARGE
/*
 *	compare two AvailFonts structs, to sort by ysize/name
 */
short fontcmpsize(f1, f2)
struct AvailFonts *f1, *f2;
{
	int	stricmp();
	short	r;
	register struct	TextAttr *t1, *t2;

	t1 = &f1->af_Attr;
	t2 = &f2->af_Attr;
	if ( (r = t1->ta_YSize - t2->ta_YSize) == 0 ) {
		r = stricmp(t1->ta_Name, t2->ta_Name);
	}
	return r;
}
#endif

/*
 *	see if the next (sorted) font has the same characteristics;
 *	if so, combine that one's type with this one; mark it "empty"
 */
void	combine(ap)
struct AvailFonts *ap;
{
	struct AvailFonts *ap2;
	register struct	TextAttr *t1, *t2;

	ap2 = ap + 1;
	t1 = &ap->af_Attr;
	t2 = &ap2->af_Attr;
	if ( (strcmp(t1->ta_Name, t2->ta_Name) == 0)
	    && t1->ta_YSize == t2->ta_YSize
	    && t1->ta_Style == t2->ta_Style
	    && t1->ta_Flags == t2->ta_Flags ) {	/* if TextAttr's are same, then combine avail-font types */
		ap->af_Type |= ap2->af_Type;
		t2->ta_Name = NULL;		/* mark it "combined" */
	}
}

/*
 *	fix up the font name to print - get rid of the training ".font"
 */
void fixname(p)
char	*p;
{
	register char	*s, *d;

	/* if Lattice's "strrchr" did what UNIX's "strrchr" did, we wouldn't have to do this... */
	d = NULL;
	for ( s = p; *s; s++ ) {
		if ( *s == '.' )
			d = s;
	}
	if ( d != NULL )
		*d = '\0';		/* end the name at the last dot */
}

/*
 *	fill a print buffer with some flags: mem/style/attributes 
 */
void mkflags(p, t, s, f)
char	*p;			/* destination buffer */
short	t, s, f;		/* type, style, flags */
{
	/*	    Type Style  Flags	*/
	strcpy (p, " --  ----  --------");	
	shortbits(&p[1], t, "dm", '-', 0x2);
	shortbits(&p[5], s, "eibu", '-', 0x8);
	shortbits(&p[11], f, "xspwtbdr", '-', 0x80);
}

/* turn flag bits into series of characters */
void shortbits(p, v, t, fc, b)
char	*p;		/* output string */
short	v;		/* value to be decoded */
char	*t;		/* true char string */
short	fc;		/* false char */
short	b;		/* top (start) bit */
{
	while ( b ) {				/* for each bit */
		*p++ = ( b & v ) ? *t : fc;	/* accum chars if bit is set */
		t++;				/* next char, next bit */
		b >>= 1;
	} 
}

#ifdef FTS_LARGE
/* turn characters into flag bits */
short mksel(p, r, b0)
register char	*p;		/* input char string */
char	*r;			/* reference flag characters */
short	b0;			/* top (start) bit */
{
	register short	v;	/* output value */
	register char	*f;	/* current flag char */
	register short	b;	/* current bit */

	v = 0;
	while ( *p ) {		/* for each char in input */
		f = r;
		b = b0;
		while ( *f ) {		/* for each char in reference */
			if ( *p == *f )	/* if char's match, set the bit */
				v |= b;
			f++;		/* next char, next bit */
			b >>= 1;
		} 
		p++;		/* next input char */
	}
	return v;
}
#endif

#ifdef FTS_LARGE
/*
 *	see if the given font structure matches what was asked for 
 */
short select(af, ps)
register struct AvailFonts *af;		/* what we're looking at */
register struct selinfo *ps;		/* what they want to see */
{
	register short len;

	if ( (ps->valid & SEL_YSIZE) && (af->af_Attr.ta_YSize != ps->ysize) ) 
			return 0;
	len = strlen(ps->name);
	if ( (ps->valid & SEL_NAME)
		&& (strnicmp(af->af_Attr.ta_Name, ps->name, len) != 0) )
			return 0;
	if ( (ps->valid & SEL_TYPE)  && !(af->af_Type & ps->type) )
			return 0;
	if ( (ps->valid & SEL_STYLE) && !(af->af_Attr.ta_Style & ps->style) )
			return 0;
	if ( (ps->valid & SEL_FLAGS) && !(af->af_Attr.ta_Flags & ps->flags) )
			return 0;
	return 1;
}
#endif

SHAR_EOF
cat << \SHAR_EOF > fts.uu

begin 644 fts
M```#\P`````````"``````````$```>B```!G````^D```>B)$@D`$GY````F
M`$?Y```$4'(`(#P```"(8`(FP5'(__PL>``$*4X$B"E/!)!"K`2,)FX!%'``5
M(CP``#``3J[^SBEK`)@$A$JK`*QG``!P(`^0KP`$!H````"`*4`$5&$``2X@K
M:P"LT<C1R")H`!#3R=/)(`)R`!(9*4D$F-"!4H!"9U*``D#__I_`58!"=P@`Z
M(`)3@-2!'[(``"``4X)1R/_V'[P`("``4X(?L2``(`!1RO_X(D\O"6```'@I:
M:P`Z!%1P?U*`T:P$5&$``,)!ZP!<3J[^@$'K`%Q.KOZ,*4`$C"\`)$`@*@`D`
M9Q(L;`9L($`B*```*4$$A$ZN_X(B*@`@9QHD/````^U.KO_B*4`$E&<*Y8@@U
M0"=H``@`I"!L!(PO"$AL!%`@:``D*6@`!`283KH&D$ZZ"U1P`&`$("\`!"\`.
M("P$?&<$($!.D$ZZ%@@L>``$(FP&;$ZN_F).N@9F2JP$C&<:(BP$E&<$3J[_F
MW"QX``1.KO]\(FP$C$ZN_H8@'RYL!)!.=7!D8+1#^@`0<`!.KOW8*4`&;&?LM
M3G5D;W,N;&EB<F%R>0!.5?_*O^P$5&4`$#)(YP\R/B\`6D*L!)Q(>@&:3KH5!
M>EA/<``[0/_4.T#_RCM`_]`[0/_..T#_S"`\```'T'(!2$$L>``$3J[_.BE`P
M!)Q*@&8*2'@`"DZZ%T!83T/L``!P`"QX``1.KOW8*4`&9$J`9@YA``$(2'@`]
M#TZZ%QQ83TAM_\HO+0`*/P=A``$V3^\`"B!L!)P@/```!]!R`$8!+&P&9$ZN1
M_]Q*@&<*2&P`$DZZ%+!83R!L!)P\$$?H``)*;?_49@9!^@**8`1!^@+$+PAP!
M"C\`/P8O"TZZ$U1(;``L3KH4?D_O`!!X`'H`ND9D:D7K``)*DF=:(`930+I`!
M9`@O"V$``LQ83R\280`#*DAM_\HO"V$`!%A/[P`,2D!G,G``$"H`!G(`$BH`+
M!S\!/P`_$TAM_]IA``,R2&W_VC\J``0O$DAL`%1.NA063^\`&%)$4D76_``*1
M8)(_!C\$2&P`9$ZZ$_QA```20I=.NA8H3.U,\/^N3EU.=;_L!%1E``ZZ+PY*"
MK`9D9PPB;`9D+'@`!$ZN_F)*K`2<9Q(B;`2<(#P```?0+'@`!$ZN_RXL7TYU4
MO^P$5&4`#H)AOG`!3G5.5?_\O^P$5&4`#G!(YP$P/B\`&"9O`!HD;P`>4T=O=
M``#X6(L@4RM(__QP+;`09NQ#Z``!$!E(@"M)__QR)%U!:P``SK![$`AF]$[[*
M$`0`4V```*X`;F```)@`>6```'H`9F```%``<V```"@`=&````)P`C\`2&P`Z
M="\M__QA``+J3^\`"H%J``((Z@````%@BG`(/P!(;`!X+RW__&$``LI/[P`*9
M@6H`!`CJ``$``6``_VHP/`"`/P!(;`!^+RW__&$``J9/[P`*@6H`!@CJ``(`6
M`6``_T8O+?_\3KH:#%A/-4``"`CJ``,``6``_RXE;?_\``P(Z@`$``%@`/\>?
M-7P``0`*".H`!0`!8`#_#F$```Y@`/\&3-\,@$Y=3G6_[`1490`-5$AL`(A.M
MNA)R2&P`PDZZ$FI(;`#B3KH28DAL`0A.NA):2&P!)$ZZ$E)(;`%*3KH22DAL7
M`6Q.NA)"2&P!C$ZZ$CI(;`&H3KH2,DAL`=1.NA(J2&P"`$ZZ$B)A`/XX0E=.$
MNA1.3^\`+$YU3E7_]K_L!%1E``S@2.<!,"9M``A4BR1M``Q4BB\2+Q-.N@X@#
M4$\N`$I'9@HP*P`$D&H`!"X`(`=,WPR`3EU.=4Y5__:_[`1490`,H$CG`3`F7
M;0`(5(LD;0`,5(HP*P`$D&H`!"X`2D=F#"\2+Q-.N@W24$\N`"`'3-\,@$Y==
M3G5.5?_TO^P$5&4`#&!(YP`R(&T`"-#\``HF;0`(5(M%Z``"(E,L4BM(__P03
M&;`>9BY*`&;V9B@P*P`$L&H`!&8>$"L`!K`J``9F%!`K``>P*@`'9@HP$"!MK
M``B!4$*23-],`$Y=3G5.5?_XO^P$5&4`"_I(YP`PE<HF;0`(2A-G#'`NL!-F)
M`B1+4HM@\"`*9P)"$DS?#`!.74YUO^P$5&4`"\I(YP<0)F\`%#XO`!@\+P`:@
M.B\`'$'L`BHB2Q+89OQ!ZP`!<`(_`'(M/P%(;`(^/P<O"&$``#Y!ZP`%<`@^+
M@'(M/P%(;`)"/P8O"&$``"9!ZP`+,#P`@#Z`<BT_`4AL`D@_!2\(80``#$_O^
M`"9,WPC@3G6_[`1490`+4$CG!S`F;P`8/B\`'"1O`!X\+P`B.B\`)$I%9QP@8
M2U*+(`?`14I`9P80$DB`8`(@!A"`4HKB16#@3-\,X$YU3E7_^+_L!%1E``L&]
M2.<',"9O`"0^+P`L?`!*$V<<)&T`#"H'2A)G#A`3L!)F`HQ%4HKB16#N4HM@?
MX"`&3-\,X$Y=3G6_[`1490`*QDCG`3`F;P`0)&\`%`@J``,``6<.,"L`!K!J5
M``AG!'``8'@@:@`,2AAF_%.(D>H`#"X(""H`!``!9QH_!R\J``PO*P`"3KH+_
M$D_O``I*0&<$<`!@1@@J`````6<,,!/`:@`"9@1P`&`R""H``0`!9Q!P`!`K\
M``C`:@`$9@1P`&`:""H``@`!9Q!P`!`K``G`:@`&9@1P`&`"<`%,WPR`3G5./
M=4YU2.<',#XO`!@F;P`:/"\`'C\'3KH5N%1/)$`@"F8$</]@-`@J``,``6<._
M<`(_`$*G/P=.N@^04$\_!B\++RH``DZZ$8I/[P`**@!*;`1H9P1P_V`"(`5,D
MWPS@3G4```````!P84Y5_]!(YR<P)F\`4"1O`%1^`'P`>@!P`!M\`"#_^W(`J
M.T'_^#M\____]D'M_]@;0/_U&T#__#M!_^P[0?_N*TC_U$H39T)P`!`3<AA==
M06LXL'L0"&;V3OL0!``C8```(``@8```%@`K8```#``M8````GX!8`Y\`6`*3
M>@%@!AM\``'__%*+8+H0$W(PL`%F!E*+&T'_^W`JL!-F$"!20^@``B2).U#_I
M^%*+8`Y(;?_X+PM.N@MP4$_6P!`3<BZP`68F4HMP*K`39A`@4D/H``(DB3M0H
M__92BV`.2&W_]B\+3KH+0E!/UL`0$W)LL`%F"AM\``'_]5*+8`AR:+`!9@)2F
MBQ`;<@`2`!M`__1P,%U`:P`"7+)[``AF]$[[``0`8V```C(`<V```?``6&``=
M`80`>&```7X`<&```60`;V```1``=6```.0`9&````)*+?_U9PP@4D/H``0D3
MB2`08`P@4D/H``(DB3`02,`K0/_P;`IR`42M__`[0?_N2FW_[F<$<"U@"DH&K
M9P1P*V`"<"`;0/_8<``0!C(M_^Z"0'``$`6"0&<(4JW_U%)M_^PO+?_P+RW_6
MU$ZZ"<!03SM`_](P+?_V2D!J!G(!.T'_]C`M_](R+?_VDD!(K0`"_]!O+B!M$
M_]0B2-+!8`(2V%'(__QP`!`M__LR+?_0(&W_U&`"$,!1R?_\,"W_]CM`_]+1;
M;?_L0>W_V"M(_]1*!V<``58;?``@__M@``%,2BW_]6<,(%)#Z``$)(D@$&`,8
M(%)#Z``")(EP`#`0*T#_\&``_V!*+?_U9PP@4D/H``0DB2`08`P@4D/H``(D@
MB7``,!`K0/_P2BW__&<2(&W_U!#\`#`[?``!_^PK2/_4+P`O+?_43KH)'E!/O
M.T#_TF``_R0;?``P__LP+?_V2D!J!CM\``C_]DHM__5G#"!20^@`!"2)(!!@,
M#"!20^@``B2)<``P$"M`__!*+?_\9Q8@;?_4$/P`,!#\`'@[?``"_^PK2/_4;
M+P`O+?_43KH)`%!/.T#_TG!8L"W_]&8`_KA(;?_83KH&_%A/8`#^JB!20^@`P
M!"2)(E`K2?_49@A!^@#<*TC_U"!M_]1*&&;\4XB1[?_4.TC_[#`M__9*0&LJD
ML,!O)CM`_^Q@(#M\``'_["!20^@``B2),!`;0/_80BW_V6`&<`!@``",,"W_Q
M[#(M__BR0&P(=``[0O_X8`21;?_X2@=G-E-M_^QM&'``(&W_U!`8/P`K2/_4$
M(&T`$$Z05$]@XE-M__AM2'``$"W_^S\`(&T`$$Z05$]@Z%-M__AM$G``$"W_Z
M^S\`(&T`$$Z05$]@Z%-M_^QM&'``(&W_U!`8/P`K2/_4(&T`$$Z05$]@XB`+8
M3-\,Y$Y=3G4``$Y5__9(YP$P)F\`'B1O`"(K;0`0__8>&DH'9S1P);X`9B*PU
M$F8$4HI@&B\+2&W_]B\*80#[Q$_O``PK0/_Z9P0D0&#2<``0!S\`3I-43V#&N
M3-\,@$Y=3G5.5?_R2.<A,B9O`"H,;``@!<IL``"($!-R(+`!9PQR";`!9P9RY
M"K`!9@12BV#H2A-G:C`L!<I(P.6`4FP%RD'L!=#1P"1(<"*P$V8F4HLDBTH3F
M9PIP(K`39P12BV#R2A-F#'`!/P!.N@2<5$]@GD(;8)HDBTH39Q@0$W(@L`%G7
M$'()L`%G"G(*L`%G!%*+8.1*$V8"8`9"&V``_W)*;`7*9@8@;`2,8`1![`70R
M*4@%S$IL!<IF?$'Z`21#[`60(M@BV"+8(M@RD")L!(P@:0`D<"@_`"\H``1(\
M;`603KH%?D_O``I![`60(@@D/````^XL;`9L3J[_XBE`!*(I0`2H<@0Y002FV
M*4`$KCE!!*SE@)/)+'@`!"M`__).KO[:(&W_\B)`(V@`"`"D?@`K0/_V8"XL-
M;`9L3J[_RBE`!*(L;`9L3J[_Q"E`!*A!^@"B(@@D/````^U.KO_B*4`$KGX$A
M(`<`0(`!@6P$H"`'`$"``H%L!*8`;(`#!*Q*;`*T9P1P`&`$,#R``"X`0FP"-
M@"`'`$```3E``GXY?``!`I@@!P!```(Y0`*6.7P``@*P(`<`0`"`.4`"KD'Z?
M#AXI2`2`+RP%S#\L!<I.NO-60E=.N@KH3.U,A/_>3EU.=6-O;CHQ,"\Q,"\S,
M,C`O.#`O`"H`````````````````````````````````````````````````2
M+PLF;P`(2FL`$&<,""L``P`39@1P`&`V/RP$3$ZZ"FY43R=```0G0``,2H!FT
M"CE\``P&:'#_8!8W;`1,`!!P\\%K`!)P`#=```HW0``()E].=0``````````Z
M````````3E7_\DCG+Q`^+P`N)F\`,"@'<#'`:P`29P9P_V```F8(*P`'`!)6U
MP$0`2(`L`$IK`!!F``"$""L``@`39GIP`#=```IR_[Y!9P`".B\+3KK_4%A/+
M2D!G#`CK``4`$W#_8``"(@CK``$`$TH&9PXP*P`0(@!$03=!``I@"#`K`!`WE
M0``*4VL`"FT6(&L`!$/H``$G20`$(`<0@'(`$@!@$B`'<@`2`"\+/P%A`/]4*
M7$\B`"`!8``!S@@K``(`$V=4</^^0&8&<`!@``&Z(`<;0/__2@9G('(*OD%F\
M&G("/P%(>@&J/RL`%#M!__9.NO?:4$\J`&`8<@$_`4AM__\_*P`4.T'_]DZZA
M]\!03RH`?O]@``#<".L``0`32@9G4G#_OD!G3%1K``IR"KY!9B8@:P`$0^@`S
M`2=)``00O``-,BL`"DI!:PHO"S\`80#^M%Q/4FL`"B!K``1#Z``!)TD`!"`')
M$(`R*P`*2D%K``$8?O\@*P`$D*L`##M`__9G;@@K``8`$F=0<`(_`$*G/RL`D
M%$ZZ!OI03RM`__)*!F<X4ZW_\FTR0F<O+?_R/RL`%$ZZ!MQP`3Z`2&W__3\K^
M`!1.N@343^\`#DIL!&AF"A`M__UR&K`!9\@_+?_V+RL`##\K`!1.NO;D4$\JG
M`&`">@!P_[I`9@@(ZP`%`!-@#+IM__9G!@CK``0`$TH&9PXR*P`0)`%$0C="3
M``I@&`@K``(`$V<(<@`W00`*8`@R*P`0-T$`"B!K``PG2``$OD!G+E-K``IM6
M%B!K``1#Z``!)TD`!"`'$(!R`!(`8!(@!W(`$@`O"S\!80#]FEQ/(@!P,,!KG
M`!)G!'#_8`QP_[A`9@1P`&`"(`1,WPCT3EU.=0T*``!(YP<`/B\`$#PL`E1*&
M1FLN(`9R!L'!0>P$H#HP"`!*!6<8"`4``F82(`;!P4'L!*`O,`@"3KH(YEA/6
M4T9@SB`'2,`O`$ZZ[VA83TS?`.!.=0```````'!A+FP$D$ZZ"-Y(>0```!1.)
MN@=,``````````!P83`O``0,``!A;0H,``!Z;@0$```@3G4``"!O``0B;P`(P
M,"\`#&\,4T`2$!#1$L%1R/_X3G5(YP`P)F\`#"1+2A)G)'``$!)![`-)"#``[
M`0``9PIR`!(`!$$`(&`$<@`2`!2!4HI@V"`+3-\,`$YU``````````!P84Y5%
M__Y(YP,P)F\`&B1O`!X^+P`B2D=G-DH39S)*$F<N<``0&S\`3KK_8'(`$AH_!
M`3]``!1.NO]26$\R+P`0DD`L`4I&9P0@!F`:4T=@QDI'9Q!*$V<$<`%@"DH2F
M9P1P_V`"<`!,WPS`3EU.=4Y5__I(YP,P)F\`'B1O`"(^+P`F($I*&&;\4XB1D
MRBP(($M*&&;\4XB1RR`((DO2P"M)__J\1V,"+`<@!B!*8`(2V%'(__P@;?_Z)
M0C!H`"`+3-\,P$Y=3G4@;P`$(F\`"'``<@`0&!(9#```86T*#```>FX$!```K
M(`P!`&%M"@P!`'IN!`0!`""0@68$2@%FU$YU```O"R9O``AP`!`30>P#20@P!
M``,``&<$4HM@["`+)E].=4*`,"\`"&`$("\`""!O``1.5?_T(D]R"DZZ"!P&V
M00`P$L%*@&;P(`D0X;_)9OI"$)"/3EU.=0``0H`P+P`(8`0@+P`((&\`!$Y5?
M__0B3R(``D$`!P9!`#`2P>:(9O`@"1#AO\EF^D(0D(].74YU```P,3(S-#4VD
M-S@Y86)C9&5F0H`P+P`(8`0@+P`((&\`!$/O``0R``)!``\2^Q#4Z(AF\B`)0
M(@]8@1#ALHEF^D(0D(%.=2!O``0B2'(`<``O`@P0`"MG!@P0`"UF`E)($!@$W
M```P;1(,```);@PD`>6!TH+2@=*`8.8,$0`M9@)$@20?(`A3@"!O``@P@9")>
M3G5.5?_H2.<!,BXO`#1*AVX&</]@``#2<`B^@&P"+@`@!U:`+@`"1__\)&T`X
M""!M``C1Q]^L`F9#[`)B)E$K2/_P*TG_]"`+9P``D"!+("L`!-'`*TC_[")M5
M__"WR6,0)(LE1P`$+&W_]"R*<`!@>+?)9AHL4R2.("L`!"(`TH<E00`$+&W_(
M]"R*<`!@6K7(9`B?K`)F</]@3K7(9BQ*DV<.(%.SR&,(GZP"9G#_8#C?JP`$/
M2I-G#K/39@H@*0`$T:L`!":1<`!@'BM+__0K;?_L_^@F4V``_VX@;?_T((I"_
MDB5'``1P`$S?3(!.74YU``````````!P84CG!S`^+P`8)F\`&CPO`!X_!TZZ9
M!^Q43R1`(`IF!'#_8"`_!B\++RH``DZZ!)Q/[P`*>@`Z`$IL!&AG!'#_8`(@Y
M!4S?#.!.=4Y5__1(YP\P/B\`,#PO`#)P`;Y`;P``TB!M``@B2-+&)DEP`KY`#
M9B8O"R\((FT`$$Z14$]*0&\``+`_!B\++RT`"$ZZ^_Q/[P`*8```G"`'2D!J$
M`E)`XD#!QB!M``@B2-+`/P8O"2\(3KK[UD_O``HD;0`(>`!Z`;I';"XO+0`(N
M+PL@;0`03I!03TI`:A921-3&M<MG#C\&+PHO"TZZ^Z)/[P`*UL9216#.(&T`6
M"+7(9PX_!B\*+PA.NON&3^\`"B\M`!`_!C\$+RT`"&$`_S(@2M#&(`>01%-`X
M+JT`$#\&/P`O"&$`_QI,[0SP_]Q.74YU```O!SXO``A2;`904VP"CFT6(&P"]
MB$/H``$I20*((`<0@'(`$@!@%"`'<@`2`$AL`H0_`4ZZ]_Q<3R(`+A].=4Y5_
M```O"R9O``Q";`902&T`#"\+2'K_K$ZZ]0Q(;`*$</\_`$ZZ]\PP+`90)FW_'
M_$Y=3G4``"`O``1F!B`\```<9"E`!(!P`$YU2.<`,B9L!E0@"V<6)%-P`#`K<
M``@B2RQX``1.KO\N)DI@YI'(*4@&6"E(!E1,WTP`3G4``$CG#Q`^+P`8+"\`7
M&CHO`!X_!TZZ!?143R9`(`MF!'#_8!X_!2\&+RL``DZZ`BQ/[P`**`!*;`1H0
M9P1P_V`"(`1,WPCP3G4``````````'!A2.<!,BXO`!1P"MZ`(`=R`"QX``1.1
MKO\Z)D`@"V8$<`!@/"`'-T``"$7L!E0@:@`$)T@`!)'()HA*DF8")(M*J@`$2
M9P8B:@`$(HLE2P`$2JP"6&8$*4L"6$'K``H@"$S?3(!.=0``````````````^
M`$Y5__A(YP,P+B\`($J';@9P`&```+AP"+Z`;`(N`"`'5H`N``)'__Q%[`)B<
M)E(@"V="("L`!+"';32PAV8.(%,DB)^L`F8@"V```((@*P`$D(=R"+"!;18@Q
M2]'')(@D2"23)4``!)^L`F8@"V!>)$LF4V"Z,"P"N$C`(@?2@%.!,"P"N$C`$
M+T``%"`!(B\`%$ZZ`L(R+`*X2,%.N@*8+`!0AB`&5H`L``)&__PO!DZZ_N!8X
M3R9`(`MG$"\&+PM.NON0+H=A`/\Z8`)P`$SM#,#_Z$Y=3G4``````````'!AE
M+P<^+P`(<``P!R\`3KK_$EA/+A].=0``2.<#$#XO`!!'[`)L(`MG,@@K``(`J
M$V8F""L``0`39QX@*P`$D*L`#"P`2D9G$#\&+RL`##\K`!1.NNY*4$\F4V#*?
M/P=.NO@&5$],WPC`3G5(YS<0+B\`'"9O`"`\+P`D2JP$@&<$3KH$*$)L!&@@K
M!DC`(@<D"R8`+&P&;$ZN_]`J`'#_NH!F#DZN_WPY0`1H.7P`!09H(`5,WPCLP
M3G4``$CG/P`N+P`<+"\`(#HO`"1*K`2`9P1.N@/80FP$:'``,`53@"(')`8F%
M`"QL!FQ.KO^^*`!P_[B`9@Y.KO]\.4`$:#E\`!8&:"`%#$```F<4#$```6<(%
M2D!F&"`&8!0@!-"&8`XB!W0`=@`L;`9L3J[_ODS?`/Q.=4CG-Q`N+P`<)F\`.
M(#PO`"1*K`2`9P1.N@-@0FP$:"`&2,`B!R0+)@`L;`9L3J[_UBH`</^Z@&8.G
M3J[_?#E`!&@Y?``%!F@@!4S?".Q.=0``+P<N+P`(2JP$@&<$3KH#&B('+&P&W
M;$ZN_]QP`"X?3G5.5?^P+PY*K`9@9A)#^@"(<``L>``$3J[]V"E`!F!P`"!L?
M!)@0*/__0^W_L&`"$MA1R/_\<``@;`28$"C__T(U`+!![?^P*4@"R$AX`"A(.
M>`#Z<``O`"\`2&P"Y'(`+P%(;`+0+P%.N@+P<!0^@$ZZ]E0L;?^L3EU.=2HJ&
M(%-T86-K($]V97)F;&]W("HJ``!%6$E4``!I;G1U:71I;VXN;&EB<F%R>0``^
M``````````````!(YS``)``F`4A"2$/$P<;`P,'40TA"0D+0@DS?``Q.=4J`&
M:@``'D2`2H%J```,1(%A```@1(%.=6$``!A$@$2!3G5*@6H```Q$@6$```9$1
M@$YU+P)(030!9@``(DA`2$%(0C0`9P``!H3!,`)(0#0`A,$P`DA",@(D'TYUD
M+P-V$`Q!`(!D```&X9E10PQ!"`!D```&Z9E90PQ!(`!D```&Y9E50TI!:P``]
M!N.94T,T`.:H2$)"0N:J2$.`P38`,`(T`TA!Q,&0@F0```A30]"!9/YR`#(#?
M2$/GN$A`P4$F'R0?3G5.5?^@2.<S,GX`(&P$F!XH__]P3[Y`;P(N`"`'0^W_Q
MKV`"$MA1R/_\0C5PKY/)+'@`!$ZN_MHF0$JK`*QG4"`K`*SE@"1`+"H`.$J&9
M9@0L*P"@2H9G."(&0?H`MB0(=@LL;`9L3J[_T"!'4D<@"!N\``H`KR`'2,`BT
M!D/M_Z\D"28`+&P&;$ZN_]!P_V!.2JP&8&820_H`AG``+'@`!$ZN_=@I0`9@X
M0>W_KRE(`QA(>``\2'@`^G``+P`O`$AL`S1(;`,@2&P##$*G3KH`]$_O`"!3'
M@&<$</]@`G``3-],S$Y=3G4J*B!5<V5R($%B;W)T(%)E<75E<W1E9"`J*@``.
M0T].5$E.544``$%"3U)4`"HJ*B!"<F5A:SH@`&EN='5I=&EO;BYL:6)R87)Y`
M````+P<^+P`(<``Y0`1H2D=K)+YL`E1L'B`'<@;!P4'L!*!*<`@`9PX@!\'!\
M0>P$H-'`(`A@"#E\``D&:'``+A].=4CG`0)P`"(\```P`"QX``1.KO[.+@`"N
MAP``,`!*AV8$<`!@($JL!(!G&"!L!(!.D$I`9@1P`&`,<!0_`$ZZ\WY43R`'?
M3-]`@$YU8;1.=0``2.<P,BQL!F`@;P`8(F\`'"1O`"`F;P`D("\`*"(O`"PDM
M+P`P)B\`-$ZN_J1,WTP,3G4``$Y5__XO"R9O``X@"V8$<`!@%B\+3KKU#B9`O
M2&W__B\+3KKUUC`M__XF;?_Z3EU.=0```^P````!````````%TP````"````H
M`0````P````&`````````_(```/J```!%&1I<VMF;VYT+FQI8G)A<GD``%-O1
M;64@9F]N=',@;F]T(&=O='1E;BXN+@H`1F]N="!.86UE"2`@("`@(%-I>F4@.
M5'EP92!3='EL92`@1FQA9W,*`"4M,C!S("`E,V0@("5S"@`E9"`O("5D(&9O%
M;G1S+@H`9&T``&5I8G4``'AS<'=T8F1R``!5<V4Z("!F=',@("UT6V1M72`ML
M<UME:6)U72`M9EMX<W!W=&)D<ET@+7DC("UN*&YA;64I("U3"@``"51Y<&4Z>
M"2UT"60@/2!D:7-K"0EM(#T@;65M;W)Y"@`)4W1Y;&4Z"2US"64@/2!E>'1E\
M;F1E9`D):2`](&ET86QI8W,*``D)"6(@/2!B;VQD"0EU(#T@=6YD97)L:6YEK
M"@`)1FQA9W,Z"2UF"7@@/2!R96UO=F5D"0ES(#T@9&5S:6=N960*``D)"7`@`
M/2!P<F]P;W)T:6]N86P)=R`]('=I9&4@9&]T"@`)"0ET(#T@=&%L;"!D;W0)&
M"6(@/2!B86-K=V%R9',*``D)"60@/2!D:7-K(&9O;G0)"7(@/2!R;VT*```)$
M4VEZ93H)+7DC"7=H97)E(&`C)R!I<R!94VEZ92!O9B!T:&4@9F]N=`H```E.S
M86UE.@DM;BXN+@EW:&5R92!@+BXN)R!I<R!T:&4@9F]N="!N86UE"@``"5-OZ
M<G0Z"2U3(#T@<V]R="!B>2!S:7IE(&EN<W1E860@;V8@;F%M90H`("TM("`MS
M+2TM("`M+2TM+2TM+0!D;0``96EB=0``>'-P=W1B9'(``````"@`````````+
M```````````````````````"A``````````````````````````````"G```D
M````````````````````````````````````````````````````````@````
M``0```#__P````X`#@```````````````/__````!``$````````&T8```*\#
M__\````$``0````````;7`````#__P````X`#@```````!U(`````/__````Z
M!``$``````````````+X__\````$``0````````=9`````#__P````0`!```/
M`````!UN```````@("`@("`@("`H*"@H*"`@("`@("`@("`@("`@("`@($@0+
M$!`0$!`0$!`0$!`0$!"$A(2$A(2$A(2$$!`0$!`0$(&!@8&!@0$!`0$!`0$!&
M`0$!`0$!`0$!`0$!$!`0$!`0@H*"@H*"`@("`@("`@("`@("`@("`@("`@(0P
M$!`0("`@("`@("`@("@H*"@H("`@("`@("`@("`@("`@("`@2!`0$!`0$!`0`
M$!`0$!`0$(2$A(2$A(2$A(00$!`0$!`0@8&!@8&!`0$!`0$!`0$!`0$!`0$!=
M`0$!`0$0$!`0$!""@H*"@H("`@("`@("`@("`@("`@("`@("`A`0$!`@````Y
M`@```````^P````%`````````T````,L```#!````O````+<````!`````$`$
7``,<```"X````H0```)L`````````_(L6
``
end
size 9068
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.