/*
 *	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

