MAG Disk (Jan 1990) : showfont.c

/*********************************************************/
/*	 ShowFont 4.0 © 1989 by Arthur Johnson Jr.	 */
/* ===================================================== */
/* Usage:  ShowFont [font_name] [font_size] [font_style] */
/* ===================================================== */
/*	       Last modifications - 07/12/89		 */
/*********************************************************/

#include "intuition/intuition.h"
#include "exec/memory.h"
#include "libraries/diskfont.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"

#define XAREA	    20	    /* no characters beyond (screenwidth - XAREA) */
#define FONTBUFFER  5000    /* hopefully enough for most people */
#define FONTNAMELEN 30	    /* maximum font name length */
#define MAXSTYLES   15	    /* that should be enough */
#define NUMSTYLES   4	    /* Underlined, Bold, Italics, Extended */

#define MAXDISPLAY  8	    /* max names, sizes, and styles to display */
#define NAMELEN     20	    /* maximum chars of name displayed */
#define NAMEXPOS    8
#define SIZEXPOS    200
#define STYLXPOS    256
#define TOPYPOS     8

#define BASELINE    6	/* for Topaz-8 font */
#define XINCR	    8
#define YINCR	    8

/* these are short little macro expanders to save me some typing effort */
#define SIZES	    fonts[namesel].sizes
#define SIZESEL     fonts[namesel].sizesel
#define STYLES	    fonts[namesel].size[SIZESEL].styles
#define STYLE	    fonts[namesel].size[SIZESEL].style
#define STYLESEL    fonts[namesel].size[SIZESEL].stylesel

extern struct DosLibrary *DOSBase;

extern struct NewScreen NewScreenStructure;
extern struct NewWindow NewWindowStructure1;
extern struct Menu Menu1;
extern struct Requester FontRequesterStructure2;
extern struct Requester AboutRequesterStructure3;
extern struct Requester FehRequesterStructure4;
extern struct Requester ScreenRequesterStructure5;
extern struct Requester DirRequesterStructure6;
extern struct PropInfo PropSInfo;
extern struct PropInfo FontFontFontSInfo;
extern struct PropInfo FontFontSizeSInfo;
extern struct PropInfo FontFontStyleSInfo;
extern struct Gadget Prop;
extern struct Gadget FontFont;
extern struct Gadget FontSize;
extern struct Gadget FontStyle;
extern struct Gadget ScreenGadget9;
extern struct Gadget ScreenGadget10;
extern struct Gadget ScreenGadget11;
extern struct Gadget ScreenGadget12;
extern struct Gadget ScreenGadget13;
extern struct Gadget ScreenGadget14;
extern struct Gadget ScreenGadget15;
extern struct Gadget ScreenGadget16;
extern struct Gadget ScreenGadget17;
extern UBYTE DirDirNewDirSIBuff[];
extern struct Gadget DirNewDir;
extern UBYTE errormessage[];
extern struct IntuiText FehIText15;

struct Screen *screen;
struct Window *window;
struct RastPort *rp;
struct Gadget *whichgad;
struct IntuiMessage *message;

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct DiskfontBase *DiskfontBase;

struct sizenode {
    UWORD ysize;
    int   styles;
    UBYTE style[MAXSTYLES];
    UBYTE flags;
    int   stylesel;
};

struct tempsizenode {
    UWORD ysize;
    int   styles;
    UBYTE style[MAXSTYLES];
    UBYTE flags;
    struct tempsizenode *prev;
    struct tempsizenode *next;
};

struct fontinfo {
    char    name[FONTNAMELEN + 1];
    int     sizes;
    struct  sizenode *size;
    int     sizesel;
};
struct fontinfo *fonts;

struct tempfontinfo {
    char    name[FONTNAMELEN + 1];
    int     sizes;
    struct  tempsizenode *size;
    struct  tempfontinfo *prev;
    struct  tempfontinfo *next;
};
struct tempfontinfo *tempfonts;

char fontname[FONTNAMELEN + 6]; /* include space for '.font\0' */
struct TextAttr myfont = { /* necessary structure for fonts */
    &fontname[0],   /* default is Topaz-8 fault */
    8,
    FS_NORMAL,
    FPF_ROMFONT };
struct TextFont *font;

struct {
    UBYTE *string;
    int length;
} fontline[256];    /* maximum of 256 lines (one char/line) */

/* I just don't want to pass the following values around */

int numfonts;

int screenwidth = 640,
    screenheight = 200,
    screendepth = 2;

int nameline, sizeline, styleline,
    namesel,
    rfontlastline, rsizelastline, rstylelastline;

USHORT fontpropsize, fontproppos, sizepropsize, sizeproppos,
	stylepropsize, styleproppos;

UBYTE qualifiers[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		       0, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0,
		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 2, 2,
		       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
		       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2,
		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 5,
		       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		       3, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 3, 3, 3, 3,
		       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 4,
		       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
		       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4,
		       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
		       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3 };

char whichkey[] = { '?', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '?', '?', '?', '?', '?',
		    's', '1', '\'', '3', '4', '5', '7', '\'',
		    '9', '0', '8', '=', ',', '-', '.', '/',
		    '0', '1', '2', '3', '4', '5', '6', '7',
		    '8', '9', ';', ';', ',', '=', '.', '/',
		    '2', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '[', '\\', ']', '6', '-',
		    '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '[', '\\', ']', '`', '?',
		    '?', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '?', '?', '?', '?', '?',
		    's', '1', '\'', '3', '4', '5', '7', '\'',
		    '9', '0', '8', '=', ',', '-', '.', '/',
		    '0', '1', '2', '3', '4', '5', '6', '7',
		    '8', '9', ';', ';', '-', '=', '.', '/',
		    '2', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '[', '\\', ']', '6', '-',
		    '\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '[', '\\', ']', '\'', 'm' };

void error(char *), clearfonts(), clearfontlines(), cleanup(char *),
     cleartempsize(struct tempfontinfo *), cleartempfonts(),
     addsizenode(struct tempfontinfo *, UWORD, UBYTE, UBYTE), readfonts(),
     setupscreen(), refresh1(struct RastPort *),
     refresh2(struct RastPort *), refresh3(struct RastPort *),
     updateprop(struct Gadget *, struct Requester *, USHORT, USHORT),
     duhprop(struct RastPort *, int), *findfonts(), makewindowtitle(char *),
     main(int, char *[]);

int stylenum(UBYTE), definescreen(), findfont(),
    propline(USHORT, int), selectfont(), changefontsdir();

USHORT proppos(int, int), propsize(int, int);

void error(char *s)
{

    ULONG class;

    strcpy(errormessage, s);
    FehIText15.LeftEdge = (100 - ((strlen(s) * XINCR) / 2));
    Request(&FehRequesterStructure4, window);

    do {
	while ((message = (struct IntuiMessage *)GetMsg(window->UserPort)) == 0) { }
	class = message->Class;
	ReplyMsg(message);
    } while (class != GADGETUP);

}

void clearfonts()
{

    register i;

    for (i = 0; i < numfonts; i++)
	FreeMem(fonts[i].size, fonts[i].sizes * sizeof(struct sizenode));
    FreeMem(fonts, numfonts * sizeof(struct fontinfo));

    numfonts = 0;   /* there are no fonts */

}

void clearfontlines()
{

    register i = 0;

    while (fontline[i].length != 0) {
	FreeMem(fontline[i].string, fontline[i].length);
	fontline[i].length = 0;
	++i;
    }

}

void cleanup(char *text)
{

    clearfontlines();
    clearfonts();
    if (font)
	CloseFont(font);
    if (window) {
	ClearMenuStrip(window);
	CloseWindow(window);
    }
    if (screen)
	CloseScreen(screen);
    if (DiskfontBase)
	CloseLibrary(DiskfontBase);
    if (IntuitionBase)
	CloseLibrary(IntuitionBase);
    if (GfxBase)
	CloseLibrary(GfxBase);

    if (text)
	puts(text);

    exit(0);

}

void cleartempsize(struct tempfontinfo *font)
{

    struct tempsizenode *erase;

    while (font->size != NULL) {
	erase = font->size;
	font->size = font->size->next;
	FreeMem(erase, sizeof(struct tempsizenode));
    }

}

void cleartempfonts()
{

    struct tempfontinfo *erase;

    while (tempfonts != NULL) {
	erase = tempfonts;
	tempfonts = tempfonts->next;
	cleartempsize(erase);
	FreeMem(erase, sizeof(struct tempfontinfo));
    }

}

int stylenum(UBYTE style)
{

    int flags = 0;

    if (style == FS_NORMAL) {
	return(0);
    }
    if (style & FSF_UNDERLINED) {
	flags |= 1;
    }
    if (style & FSF_BOLD) {
	flags |= 2;
    }
    if (style & FSF_ITALIC) {
	flags |= 4;
    }
    if (style & FSF_EXTENDED) {
	flags |= 8;
    }

    return(flags);

}

void addsizenode(struct tempfontinfo *font, UWORD ysize, UBYTE style, UBYTE flags)
{

    struct tempsizenode *search, *prev, *newnode;

    int foundit = FALSE;

    prev = NULL;
    search = font->size;
    while (search != NULL) {
	if ((search->ysize == ysize) && (search->flags == flags)) {
	    search->style[search->styles] = stylenum(style);
	    ++(search->styles);
	    foundit = TRUE;
	    break;
	}
	else {
	    if (search->ysize >= ysize) /* should go before this size */
		break;
	    else {
		prev = search;
		search = search->next;
	    }
	}
    }
    if (!foundit) {
	newnode = (struct tempsizenode *)AllocMem(sizeof(struct tempsizenode), MEMF_CLEAR);
	if (newnode == NULL)
	    cleanup("ShowFont: couldn't allocate 'tempsizenode' memory!");
	newnode->ysize = ysize;
	newnode->styles = 0;
	newnode->flags = flags;
	newnode->style[newnode->styles] = stylenum(style);
	++(newnode->styles);
	++(font->sizes);
	if (font->size == NULL) {   /* list is empty */
	    newnode->prev = NULL;
	    newnode->next = NULL;
	    font->size = newnode;
	}
	else {
	    if (search == NULL) {   /* add at end o' list */
		prev->next = newnode;
		newnode->prev = prev;
		newnode->next = NULL;
	    }
	    else {
		if (search->prev == NULL) { /* add at beginning o' list */
		    newnode->prev = NULL;
		    newnode->next = search;
		    search->prev = newnode;
		    font->size = newnode;
		}
		else {	/* add in the middle o' the list */
		    search->prev->next = newnode;
		    newnode->prev = search->prev;
		    newnode->next = search;
		    search->prev = newnode;
		}
	    }
	}
    }

}

void readfonts()
{

    struct AvailFontsHeader *afh;
    struct AvailFonts *af;

    struct tempfontinfo *search, *prev, *newnode;
    struct tempsizenode *sizenode;

    char searchname[FONTNAMELEN + 6];

    int foundit;

    int mem = FONTBUFFER,
	moremem;

    register i, j, k;

    clearfonts();

    do {
	afh = (struct AvailFontsHeader *)AllocMem(mem, MEMF_CLEAR);
	if (afh == NULL)
	    cleanup("ShowFont: couldn't allocate 'AvailFontsHeader' memory!");
	moremem = AvailFonts(afh, mem, 0xFF);
	if (moremem != 0) {
	    FreeMem(afh, mem);
	    mem += moremem;
	    printf("ShowFont: allocating %d bytes of memory for the FONTS: info.\n", mem);
	}
    } while (moremem != 0);

    if (afh->afh_NumEntries == 0) {
	FreeMem(afh, mem);
	cleanup("ShowFont: couldn't find any fonts! (Is FONTS: set correctly?)");
    }

    tempfonts = NULL;

    af = (struct AvailFonts *)&afh[1];
    for (i = 0; i < afh->afh_NumEntries; i++) {
	if (!((af->af_Attr.ta_Flags & FPF_REMOVED) ||
	    (af->af_Attr.ta_Flags & FPF_REVPATH) ||
	    ((af->af_Type & AFF_MEMORY) &&
	     (af->af_Attr.ta_Flags & FPF_DISKFONT)))) {

	    prev = NULL;
	    search = tempfonts;
	    foundit = FALSE;
	    while (search != NULL) {
		strcpy(searchname, search->name);
		strcat(searchname, ".font");
		if (stricmp(af->af_Attr.ta_Name, searchname) == 0) {
		    addsizenode(search, af->af_Attr.ta_YSize, af->af_Attr.ta_Style, af->af_Attr.ta_Flags);
		    foundit = TRUE;
		    break;
		}
		else {
		    if (stricmp(af->af_Attr.ta_Name, searchname) < 0)
			break;	/* should go before here */
		    else {
			prev = search;
			search = search->next;
		    }
		}
	    }
	    if (!foundit) {
		newnode = (struct tempfontinfo *)AllocMem(sizeof(struct tempfontinfo), MEMF_CLEAR);
		if (newnode == NULL)
		    cleanup("ShowFont: couldn't allocate 'tempfontinfo' memory!");
		stccpy(newnode->name, af->af_Attr.ta_Name, (strlen(af->af_Attr.ta_Name) - 4));
		newnode->sizes = 0;
		newnode->size = NULL;
		addsizenode(newnode, af->af_Attr.ta_YSize, af->af_Attr.ta_Style, af->af_Attr.ta_Flags);
		++numfonts;
		if (tempfonts == NULL) { /* list is empty */
		    newnode->prev = NULL;
		    newnode->next = NULL;
		    tempfonts = newnode;
		}
		else {
		    if (search == NULL) {   /* should add at end */
			prev->next = newnode;
			newnode->prev = prev;
			newnode->next = NULL;
		    }
		    else {
			if (search->prev == NULL) { /* add at beginning */
			    newnode->prev = NULL;
			    newnode->next = search;
			    search->prev = newnode;
			    tempfonts = newnode;
			}
			else { /* add in the middle o' the list */
			    search->prev->next = newnode;
			    newnode->prev = search->prev;
			    newnode->next = search;
			    search->prev = newnode;
			}
		    }
		}
	    }
	}
	af++;
    }

    FreeMem(afh, mem);

    if ((fonts = (struct fontinfo *)AllocMem(numfonts * sizeof(struct fontinfo), MEMF_CLEAR)) == NULL)
	cleanup("ShowFont: couldn't allocate 'fontinfo' memory!");

    search = tempfonts;

    for (i = 0; i < numfonts; i++) {
	strcpy(fonts[i].name, search->name);
	fonts[i].sizes = search->sizes;
	if ((fonts[i].size = (struct sizenode *)AllocMem(fonts[i].sizes * sizeof(struct sizenode), MEMF_CLEAR)) == NULL)
	    cleanup("ShowFont: couldn't allocate 'sizenode' memory!");
	sizenode = search->size;
	for (j = 0; j < fonts[i].sizes; j++) {
	    fonts[i].size[j].ysize = sizenode->ysize;
	    fonts[i].size[j].styles = sizenode->styles;
	    for (k = 0; k < fonts[i].size[j].styles; k++)
		fonts[i].size[j].style[k] = sizenode->style[k];
	    fonts[i].size[j].stylesel = 0;
	    fonts[i].size[j].flags = sizenode->flags;
	    sizenode = sizenode->next;
	}
	fonts[i].sizesel = 0;
	search = search->next;
    }

    cleartempfonts();

}

int definescreen()
{

    struct RastPort *rp;

    int newscreenheight, newscreenwidth, newscreendepth,
	y,
	refresh = TRUE;

    ULONG   class;
    struct Gadget *whichgad;

    newscreenheight = screenheight;
    newscreenwidth = screenwidth;
    newscreendepth = screendepth;

    Request(&ScreenRequesterStructure5, window);
    rp = ScreenRequesterStructure5.ReqLayer->rp;

    while (0 == 0) {

	if (refresh) {

	    SetAPen(rp, 0); /* clear those stupid gadgets! */
	    for (y = 8; y <= 71; y += 21) {
		RectFill(rp, 16, y, 93, y + 10);
	    }
	    for (y = 8; y <= 72; y += 16) {
		RectFill(rp, 206, y, 283, y + 10);
	    }

	    /* non-elegant, but it works */

	    ScreenGadget9.Flags = NULL;
	    ScreenGadget10.Flags = NULL;
	    ScreenGadget11.Flags = NULL;
	    ScreenGadget12.Flags = NULL;
	    ScreenGadget13.Flags = NULL;
	    ScreenGadget14.Flags = NULL;
	    ScreenGadget15.Flags = NULL;
	    ScreenGadget16.Flags = NULL;
	    ScreenGadget17.Flags = NULL;

	    if ((newscreenwidth == 320) && (newscreenheight == 200)) {
		ScreenGadget9.Flags = SELECTED;
	    }
	    if ((newscreenwidth == 320) && (newscreenheight == 400)) {
		ScreenGadget10.Flags = SELECTED;
	    }
	    if ((newscreenwidth == 640) && (newscreenheight == 200)) {
		ScreenGadget11.Flags = SELECTED;
	    }
	    if ((newscreenwidth == 640) && (newscreenheight == 400)) {
		ScreenGadget12.Flags = SELECTED;
	    }

	    switch (newscreendepth) {
		case 1: ScreenGadget13.Flags = SELECTED;
			break;
		case 2: ScreenGadget14.Flags = SELECTED;
			break;
		case 3: ScreenGadget15.Flags = SELECTED;
			break;
		case 4: ScreenGadget16.Flags = SELECTED;
			break;
		case 5: ScreenGadget17.Flags = SELECTED;
			break;
	    }

	    if (newscreenwidth == 640) {
		OffGadget(&ScreenGadget17, window, &ScreenRequesterStructure5);
	    }
	    else {
		OnGadget(&ScreenGadget17, window, &ScreenRequesterStructure5);
	    }

	    refresh = FALSE;

	}

	message = (struct IntuiMessage *)GetMsg(window->UserPort);
	if (message != 0) {
	    class = message->Class;
	    whichgad = (struct Gadget *)message->IAddress;
	    ReplyMsg(message);

	    if (class == GADGETUP) {
		refresh = TRUE;
		if (whichgad->GadgetID <= 5) {
		    newscreendepth = whichgad->GadgetID;
		}
		switch (whichgad->GadgetID) {
		    case 11: newscreenwidth = 320;
			     newscreenheight = 200;
			     break;
		    case 12: newscreenwidth = 320;
			     newscreenheight = 400;
			     break;
		    case 13: newscreenwidth = 640;
			     newscreenheight = 200;
			     if (newscreendepth == 5) {
				newscreendepth = 4;
			     }
			     break;
		    case 14: newscreenwidth = 640;
			     newscreenheight = 400;
			     if (newscreendepth == 5) {
				newscreendepth = 4;
			     }
			     break;
		    case 101: screenheight = newscreenheight;
			      screenwidth = newscreenwidth;
			      screendepth = newscreendepth;
			      return(TRUE);
			      break;
		    case 102: return(FALSE);
			      break;
		}
	    }
	}
    }

}

void setupscreen()
{

    if (window) {
	ClearMenuStrip(window);
	CloseWindow(window);
    }
    if (screen)
	CloseScreen(screen);

    NewScreenStructure.Width = screenwidth;
    NewScreenStructure.Height = screenheight;
    NewScreenStructure.Depth = screendepth;

    if (screenwidth == 320)
	if (screenheight == 200)
	    NewScreenStructure.ViewModes = NULL;
	else
	    NewScreenStructure.ViewModes = LACE;
    else
	if (screenheight == 200)
	    NewScreenStructure.ViewModes = HIRES;
	else
	    NewScreenStructure.ViewModes = HIRES | LACE;

    NewWindowStructure1.Width = screenwidth;
    NewWindowStructure1.Height = screenheight;

    if (screenwidth == 320) {
	FontRequesterStructure2.LeftEdge = 0;
	AboutRequesterStructure3.LeftEdge = 56;
	FehRequesterStructure4.LeftEdge = 60;
	ScreenRequesterStructure5.LeftEdge = 10;
	DirRequesterStructure6.LeftEdge = 60;
    }
    else {
	FontRequesterStructure2.LeftEdge = 160;
	AboutRequesterStructure3.LeftEdge = 216;
	FehRequesterStructure4.LeftEdge = 220;
	ScreenRequesterStructure5.LeftEdge = 170;
	DirRequesterStructure6.LeftEdge = 220;
    }

    if (screenheight == 200) {
	FontRequesterStructure2.TopEdge = 50;
	AboutRequesterStructure3.TopEdge = 50;
	FehRequesterStructure4.TopEdge = 75;
	ScreenRequesterStructure5.TopEdge = 55;
	DirRequesterStructure6.TopEdge = 75;
    }
    else {
	FontRequesterStructure2.TopEdge = 150;
	AboutRequesterStructure3.TopEdge = 150;
	FehRequesterStructure4.TopEdge = 175;
	ScreenRequesterStructure5.TopEdge = 155;
	DirRequesterStructure6.TopEdge = 175;
    }

    if ((screen = (struct Screen *)OpenScreen(&NewScreenStructure)) == NULL)
	cleanup("ShowFont: couldn't open the screen.");
    NewWindowStructure1.Screen = screen;

    if ((window = (struct Window *)OpenWindow(&NewWindowStructure1)) == NULL)
	cleanup("ShowFont: couldn't open the window.");
    rp = window->RPort;

    SetMenuStrip(window, &Menu1);

}

int findfont()
{

    char searchname[FONTNAMELEN + 6];

    int low, mid, high;

    low = 0;
    high = numfonts - 1;
    while (low <= high) {
	mid = (low + high) / 2;
	strcpy(searchname, fonts[mid].name);
	strcat(searchname, ".font");
	if (stricmp(fontname, searchname) < 0)
	    high = mid - 1;
	else
	    if (stricmp(fontname, searchname) > 0)
		low = mid + 1;
	    else
		return(mid);
    }

    return(0);

}

USHORT proppos(int line, int maxline)
{

    if (maxline == 1)
	return((USHORT)MAXBODY);
    else
	return((USHORT)((MAXBODY * (line - 1)) / (maxline - 1)));

}

USHORT propsize(int display, int maxdisplay)
{

    if (display >= maxdisplay)
	return((USHORT)MAXPOT);
    else
	return((USHORT)((MAXPOT * display) / maxdisplay));

}

int propline(USHORT proppos, int maxline)
{

    return((proppos * maxline) / MAXPOT);

}

void refresh1(struct RastPort *rp)
{

    register loop,
	     x = NAMEXPOS,
	     y = TOPYPOS + BASELINE;

    char namestring[NAMELEN + 1];

    SetAPen(rp, 1);
    SetBPen(rp, 0);

    for (loop = 0; loop < MAXDISPLAY; loop++) {
	Move(rp, x, y);
	SetDrMd(rp, JAM2);
	if ((nameline + loop) < numfonts) {
	    if ((nameline + loop) == namesel)
		SetDrMd(rp, JAM2 | INVERSVID);
	    sprintf(namestring, "%-20s", fonts[nameline + loop].name);
	    Text(rp, namestring, NAMELEN);
	}
	else
	    Text(rp, "                    ", NAMELEN);
	y += YINCR;
    }

}

void refresh2(struct RastPort *rp)
{

    register loop,
	     x = SIZEXPOS,
	     y = TOPYPOS + BASELINE;

    char sizestring[3 + 1];

    for (loop = 0; loop < MAXDISPLAY; loop++) {
	Move(rp, x, y);
	SetDrMd(rp, JAM2);
	SetBPen(rp, 0);
	if ((sizeline + loop) < SIZES) {
	    SetAPen(rp, 1);
	    if (fonts[namesel].size[sizeline + loop].flags & FPF_PROPORTIONAL)
		SetAPen(rp, 3);
	    if ((sizeline + loop) == SIZESEL)
		SetDrMd(rp, JAM2 | INVERSVID);
	    sprintf(sizestring, "%3d", fonts[namesel].size[sizeline + loop].ysize);
	    Text(rp, sizestring, 3);
	}
	else
	    Text(rp, "   ", 3);
	y += YINCR;
    }

}

void refresh3(struct RastPort *rp)
{

    register x = STYLXPOS,
	     y = TOPYPOS + BASELINE,
	     loop, i;

    unsigned char styles[NUMSTYLES];

    SetAPen(rp, 1);
    SetBPen(rp, 0);

    for (loop = 0; loop < MAXDISPLAY; loop++) {
	Move(rp, x, y);
	SetDrMd(rp, JAM2);
	if ((loop + styleline) == STYLESEL)
	    SetDrMd(rp, JAM2 | INVERSVID);
	if ((loop + styleline) < STYLES) {
	    for (i = 0; i < NUMSTYLES; i++) {
		styles[i] = '-';
	    }
	    if ((STYLE[(loop + styleline)] | 1) == STYLE[(loop + styleline)]) {
		styles[0] = 'U';
	    }
	    if ((STYLE[(loop + styleline)] | 2) == STYLE[(loop + styleline)]) {
		styles[1] = 'B';
	    }
	    if ((STYLE[(loop + styleline)] | 4) == STYLE[(loop + styleline)]) {
		styles[2] = 'I';
	    }
	    if ((STYLE[(loop + styleline)] | 8) == STYLE[(loop + styleline)]) {
		styles[3] = 'E';
	    }
	    Text(rp, styles, NUMSTYLES);
	}
	else {
	    for (i = 0; i < NUMSTYLES; i++) {
		styles[i] = ' ';
	    }
	    Text(rp, styles, NUMSTYLES);
	}
	y += YINCR;
    }

}

void updateprop(struct Gadget *prop, struct Requester *requester, USHORT proppos, USHORT propsize)
{

    NewModifyProp(prop, window, requester, AUTOKNOB | FREEVERT, -1, proppos, -1, propsize, 1);

}

/* updatetype =  1 : refresh 1	    */
/*		     update 2, 3    */
/*		     refresh 2, 3   */
/*		 2 : refresh 2	    */
/*		     update 3	    */
/*		     refresh 3	    */
/*		 3 : refresh 3	    */
/*		10 : arrow 1, u/r 1 */
/*		20 : arrow 2, u/r 2 */
/*		30 : arrow 3, u/r 3 */
void duhprop(struct RastPort *rp, int updatetype)
{

    if (updatetype == 1) {
	rsizelastline = (SIZES - MAXDISPLAY) + 1;
	if (rsizelastline < 1)
	    rsizelastline = 1;
	sizeline = SIZESEL;
	if ((sizeline + 1) >= rsizelastline)
	    sizeline = rsizelastline - 1;
	sizepropsize = propsize(MAXDISPLAY, SIZES);
	sizeproppos = proppos((sizeline + 1), rsizelastline);
    }
    if (updatetype < 3) {
	rstylelastline = (STYLES - MAXDISPLAY) + 1;
	if (rstylelastline < 1)
	    rstylelastline = 1;
	styleline = STYLESEL;
	if ((styleline + 1) >= rstylelastline)
	    styleline = rstylelastline - 1;
	stylepropsize = propsize(MAXDISPLAY, STYLES);
	styleproppos = proppos((styleline + 1), rstylelastline);
    }

    switch (updatetype) {
	 case 10: fontproppos = proppos((nameline + 1), rfontlastline);
		  break;
	 case 20: sizeproppos = proppos((sizeline + 1), rsizelastline);
		  break;
	 case 30: styleproppos = proppos((styleline + 1), rstylelastline);
		  break;
    }

    switch (updatetype) {
	case  1: refresh1(rp);
		 updateprop(&FontSize, &FontRequesterStructure2, sizeproppos, sizepropsize);
		 updateprop(&FontStyle, &FontRequesterStructure2, styleproppos, stylepropsize);
		 refresh2(rp);
		 refresh3(rp);
		 break;
	case  2: refresh2(rp);
		 updateprop(&FontStyle, &FontRequesterStructure2, styleproppos, stylepropsize);
		 refresh3(rp);
		 break;
	case  3: refresh3(rp);
		 break;
	case 10: updateprop(&FontFont, &FontRequesterStructure2, fontproppos, fontpropsize);
		 refresh1(rp);
		 break;
	case 20: updateprop(&FontSize, &FontRequesterStructure2, sizeproppos, sizepropsize);
		 refresh2(rp);
		 break;
	case 30: updateprop(&FontStyle, &FontRequesterStructure2, styleproppos, stylepropsize);
		 refresh3(rp);
		 break;
    }

}

int selectfont()
{

    struct RastPort *rp;

    int returnvalue,
	refreshhow,
	x, y;

    ULONG  class;

    Request(&FontRequesterStructure2, window);
    rp = FontRequesterStructure2.ReqLayer->rp;

    namesel = findfont();

    rfontlastline = numfonts - MAXDISPLAY + 1;
    if (rfontlastline < 1)
	rfontlastline = 1;
    fontpropsize = propsize(MAXDISPLAY, numfonts);

    nameline = namesel;
    if ((nameline + 1) >= rfontlastline)
	nameline = rfontlastline - 1;

    duhprop(rp, 1);
    duhprop(rp, 10);

    refreshhow = 0;
    returnvalue = 0;

    while (returnvalue == 0) {
	switch (refreshhow) {
	    case 11: if (nameline > 0)
			 --nameline;
		     duhprop(rp, 10);
		     break;
	    case 12: nameline = propline(FontFontFontSInfo.VertPot, rfontlastline);
		     if ((nameline + 1) >= rfontlastline)
			 nameline = rfontlastline - 1;
		     refresh1(rp);
		     break;
	    case 13: if ((nameline + 1) < rfontlastline)
			 ++nameline;
		     duhprop(rp, 10);
		     break;
	    case 21: if (sizeline > 0)
			 --sizeline;
		     duhprop(rp, 20);
		     break;
	    case 22: sizeline = propline(FontFontSizeSInfo.VertPot, rsizelastline);
		     if ((sizeline + 1) >= rsizelastline)
			 sizeline = rsizelastline - 1;
		     refresh2(rp);
		     break;
	    case 23: if ((sizeline + 1) < rsizelastline)
			 ++sizeline;
		     duhprop(rp, 20);
		     break;
	    case 31: if (styleline > 0)
			 --styleline;
		     duhprop(rp, 30);
		     break;
	    case 32: styleline = propline(FontFontStyleSInfo.VertPot, rstylelastline);
		     if ((styleline + 1) >= rstylelastline)
			 styleline = rstylelastline - 1;
		     refresh3(rp);
		     break;
	    case 33: if ((styleline + 1) < rstylelastline)
			 ++styleline;
		     duhprop(rp, 30);
		     break;
	}

	message = (struct IntuiMessage *)GetMsg(window->UserPort);
	if (message != 0) {
	    class = message->Class;
	    x = message->MouseX - FontRequesterStructure2.LeftEdge;
	    y = message->MouseY - FontRequesterStructure2.TopEdge;
	    whichgad = (struct Gadget *)message->IAddress;
	    ReplyMsg(message);

	    if (class == GADGETDOWN) {
		refreshhow = whichgad->GadgetID;
	    }
	    if (class == GADGETUP) {
		switch (whichgad->GadgetID) {
		    case 101: returnvalue = 1;
			      break;
		    case 102: returnvalue = -1;
			      break;
		}
		refreshhow = 0; /* no more auto-scrolling */
	    }
	    if (class == MOUSEBUTTONS) {
		if ((y >= 0) && (y < 100) && (x >= 0) && (x < 320)) {
		    y -= TOPYPOS;
		    y = y / YINCR;
		    if (y < MAXDISPLAY) {
			if ((x >= NAMEXPOS) && (x < (NAMEXPOS + NAMELEN * XINCR))) {
			    if (namesel == (nameline + y)) {
				returnvalue = 1;
				EndRequest(&FontRequesterStructure2, window);
			    }
			    else {
				namesel = nameline + y;
				if (namesel >= numfonts)
				    namesel = numfonts - 1;
				duhprop(rp, 1);
			    }
			}
			if ((x >= SIZEXPOS) && (x < (SIZEXPOS + 3 * XINCR))) {
			    if (SIZESEL == (sizeline + y)) {
				returnvalue = 1;
				EndRequest(&FontRequesterStructure2, window);
			    }
			    else {
				SIZESEL = sizeline + y;
				if (SIZESEL >= SIZES)
				    SIZESEL = SIZES - 1;
				duhprop(rp, 2);
			    }
			}
			if ((x >= STYLXPOS) && (x < (STYLXPOS + 4 * XINCR))) {
			    if (STYLESEL == (styleline + y)) {
				returnvalue = 1;
				EndRequest(&FontRequesterStructure2, window);
			    }
			    else {
				STYLESEL = styleline + y;
				if (STYLESEL >= STYLES)
				    STYLESEL = STYLES - 1;
				duhprop(rp, 3);
			    }
			}
		    }
		}
	    }
	}
    }

    if (returnvalue == 1) {
	strcpy(fontname, fonts[namesel].name);
	strcat(fontname, ".font");
	myfont.ta_YSize = fonts[namesel].size[SIZESEL].ysize;
	myfont.ta_Style = STYLE[STYLESEL];
	myfont.ta_Flags = fonts[namesel].size[SIZESEL].flags;
    }

    return(returnvalue);

}

void *findfonts()
{

    struct RootNode *root;
    struct DosInfo *info;
    struct DevInfo *dev_node;

    char *name;

    root=(struct RootNode *)DOSBase->dl_Root;
    info=(struct DosInfo *)BADDR(root->rn_Info);

    Forbid();

    for (dev_node = (struct DevInfo *)BADDR(info->di_DevInfo);
	 dev_node->dvi_Next != NULL;
	 dev_node = (struct DevInfo *)BADDR(dev_node->dvi_Next)) {

	name = (char *)BADDR(dev_node->dvi_Name);
	if ((*name == 5) && (strnicmp((name + 1), "FONTS", 5) == 0)) {
	    return(dev_node);
	}

    }

    return(NULL);

}

int changefontsdir()
{

    struct DevInfo *info;
    struct FileLock *lock;

    ULONG class;
    struct Gadget *whichgad;

    int quit;

    Request(&DirRequesterStructure6, window);
    Delay(10);
    ActivateGadget(&DirNewDir, window, &DirRequesterStructure6);

    quit = FALSE;

    while (!quit) {
	while ((message = (struct IntuiMessage *)GetMsg(window->UserPort)) == 0) { }
	class = message->Class;
	whichgad = (struct Gadget *)message->IAddress;
	ReplyMsg(message);

	if (class == GADGETUP) {
	    quit = whichgad->GadgetID;
	}
    }

    if (quit == 101) {
	lock = (struct FileLock *)Lock(DirDirNewDirSIBuff, ACCESS_READ);
	if (lock == NULL) {
	    error("DIR NOT FOUND!");
	    return(FALSE);
	}
	else {
	    info = (struct DevInfo *)findfonts();
	    UnLock(info->dvi_Lock);
	    info->dvi_Lock = (BPTR)lock;
	    info->dvi_Task = (APTR)(DeviceProc(DirDirNewDirSIBuff));
	    Permit();
	    return(TRUE);
	}
    }
    else {
	return(FALSE);
    }

}

void makewindowtitle(char *s)
{

    stccpy(s, fontname, (strlen(fontname) - 4));
    sprintf(s + strlen(s), " - %d", myfont.ta_YSize);
    if ((myfont.ta_Style | 1) == myfont.ta_Style) {
	strcat(s, "u");
    }
    if ((myfont.ta_Style | 2) == myfont.ta_Style) {
	strcat(s, "b");
    }
    if ((myfont.ta_Style | 4) == myfont.ta_Style) {
	strcat(s, "i");
    }
    if ((myfont.ta_Style | 8) == myfont.ta_Style) {
	strcat(s, "e");
    }

}

void main(int argc, char *argv[])
{

    int cont = TRUE,
	rethinkfont = TRUE;

    ULONG   class;
    USHORT  code;

    int line, maxline,
	yarea,
	startchar,
	len, pixels,
	whichmenu, whichitem,
	wproplastline,
	refreshhow,
	x, y;

    char windowtitle[81];

    USHORT numy, pos, size;

    register loop, i;

    UBYTE textline[256], character;

    if ((argc == 2) && (*argv[1] == '?'))
	cleanup("Usage: ShowFont [font_name] [font_size] [ubie]");

    if ((IntuitionBase = (struct IntuitionBase *)
	    OpenLibrary("intuition.library", 0)) == NULL)
	cleanup("ShowFont: couldn't open 'intuition.library'.");

    if ((GfxBase = (struct GfxBase *)
	    OpenLibrary("graphics.library", 0)) == NULL)
	cleanup("ShowFont: couldn't open 'graphics.library'.");

    if ((DiskfontBase = (struct DiskfontBase *)
	    OpenLibrary("diskfont.library", 0)) == NULL)
	cleanup("ShowFont: couldn't open 'diskfont.library'.");

    setupscreen();

    fonts = NULL;

    if (argc == 1)
	strcpy(fontname, "Topaz.font");
    else
	sprintf(fontname, "%s.font", argv[1]);
    if (argc < 3) {
	readfonts();
	selectfont();
    }
    else {
	myfont.ta_YSize = atoi(argv[2]);
	if (argc == 4) {
	    myfont.ta_Style = FS_NORMAL;
	    for (loop = 0; loop < strlen(argv[3]); loop++) {
		switch (*(argv[3] + loop) & ~0x20) {
		    case 'U': myfont.ta_Style |= 1;
			      break;
		    case 'B': myfont.ta_Style |= 2;
			      break;
		    case 'I': myfont.ta_Style |= 4;
			      break;
		    case 'E': myfont.ta_Style |= 8;
			      break;
		}
	    }
	}
    }

    for (i = 0; i < 256; ++i)
	fontline[i].length = 0;

    while (cont) {

	if (rethinkfont) {

	    if (font)
		CloseFont(font);

	    if ((font = (struct TextFont *)OpenDiskFont(&myfont)) == NULL) {
		error("FONT NOT FOUND!");
		strcpy(fontname, "Topaz.font");
		myfont.ta_YSize = 8;
		myfont.ta_Style = FS_NORMAL;
		myfont.ta_Flags = FPF_ROMFONT;
		if ((font = (struct TextFont *)OpenDiskFont(&myfont)) == NULL) {
		    cleanup("ShowFont: couldn't default to Topaz-8!");
		}
	    }

	    if (myfont.ta_YSize > screenheight) {
		screenheight = 400;
		setupscreen();
	    }

	    SetFont(rp, font);

	    clearfontlines();

	    startchar = font->tf_LoChar;
	    line = 0;
	    len = 0;

	    for (loop = font->tf_LoChar; loop <= font->tf_HiChar; ++loop) {
		textline[len++] = loop;
		pixels = TextLength(rp, textline, len) + window->BorderLeft;
		if (pixels > (screenwidth - XAREA)) {
		    --len;
		    fontline[line].string = (UBYTE *)AllocMem(len, MEMF_CLEAR);
		    if (fontline[line].string == NULL)
			cleanup("ShowFont: couldn't allocate 'fontline' memory!");
		    for (i = startchar; i < loop; ++i)
			fontline[line].string[i - startchar] = i;
		    fontline[line].length = len;
		    startchar = loop;
		    ++line;
		    len = 0;
		    --loop; /* must go back and insert that character */
		}
		else
		    if (loop == font->tf_HiChar) {
			fontline[line].string = (UBYTE *)AllocMem(len, MEMF_CLEAR);
			if (fontline[line].string == NULL)
			    cleanup("ShowFont: couldn't allocate 'fontline' memory!");
			for (i = startchar; i <= font->tf_HiChar; ++i)
			    fontline[line].string[i - startchar] = i;
			fontline[line].length = len;
			++line;
		    }
	    }

	    maxline = line;
	    line = 0;
	    yarea = screenheight - (window->BorderTop + window->BorderBottom);
	    numy = yarea / font->tf_YSize;

	    wproplastline = (maxline - numy) + 1;
	    if (wproplastline < 1)
		wproplastline = 1;

	    size = propsize(numy, maxline);

	    makewindowtitle(windowtitle);

	    makewindowtitle(windowtitle);
	    SetWindowTitles(window, windowtitle, -1);

	    rethinkfont = FALSE;
	    refreshhow = 99;	/* refresh it once only */
	}

	switch (refreshhow) {
	    case  1: if (line > 0)
			 --line;
		     break;
	    case  2: pos = PropSInfo.VertPot;
		     line = propline(pos, wproplastline);
		     if ((line + 1) > wproplastline)
			 line = wproplastline - 1;
		     break;
	    case  3: if ((line + 1) < wproplastline)
			 ++line;
		     break;
	}

	if (refreshhow != 0) {

	    pos = proppos((line + 1), wproplastline);

	    if (refreshhow != 2)    /* don't blink the slider */
		NewModifyProp(&Prop, window, NULL, AUTOKNOB | FREEVERT, -1, pos, -1, size, 1);

	    WaitTOF();  /* might just possibly reduce blinking */
	    SetAPen(rp, 0); /* clear the screen */
	    RectFill(rp, window->BorderLeft, window->BorderTop, (screenwidth - XAREA), (screenheight - window->BorderBottom));

	    SetAPen(rp, 1);
	    SetBPen(rp, 0);
	    SetDrMd(rp, JAM2);

	    for (i = line; i < (line + numy); ++i) {
		Move(rp, window->BorderLeft, ((i - line) * font->tf_YSize) + window->BorderTop + font->tf_Baseline);
		Text(rp, fontline[i].string, fontline[i].length);
	    }

	    if (refreshhow == 99)
		refreshhow = 0; /* for initial refreshment only */
	}

	message = (struct IntuiMessage *)GetMsg(window->UserPort);
	if (message != 0) {
	    class = message->Class;
	    code  = message->Code;
	    whichgad = (struct Gadget *)message->IAddress;
	    x = message->MouseX;
	    y = message->MouseY;
	    ReplyMsg(message);

	    if (class == CLOSEWINDOW)
		cont = FALSE;
	    if (class == GADGETDOWN)
		refreshhow = whichgad->GadgetID;
	    if (class == GADGETUP)
		refreshhow = 0; /* stop scrolling */
	    if (class == MOUSEBUTTONS) {
		y -= window->BorderTop;
		y /= font->tf_YSize;
		if (y < maxline) {
		    for (loop = 0; loop < fontline[(y + line)].length; loop++) {
			textline[loop] = fontline[(y + line)].string[loop];
			pixels = TextLength(rp, textline, (loop + 1)) + window->BorderLeft;
			if (x <= pixels)
			    break;
		    }
		    character = fontline[(y + line)].string[loop];
		    switch (qualifiers[character]) {
			case 0: textline[0] = '\0';
				break;
			case 1: strcpy(textline, "CONTROL-");
				break;
			case 2: strcpy(textline, "SHIFT-");
				break;
			case 3: strcpy(textline, "ALT-");
				break;
			case 4: strcpy(textline, "ALT-SHIFT-");
				break;
			case 5: strcpy(textline, "CONTROL-ALT-");
				break;
		    }
		    len = strlen(textline);
		    textline[len] = whichkey[character];
		    textline[len + 1] = '\0';
		    makewindowtitle(windowtitle);
		    strcat(windowtitle, " (");
		    strcat(windowtitle, textline);
		    sprintf(windowtitle + strlen(windowtitle),
			") [Dec %03d Hex $%02x]", character, character);
		    SetWindowTitles(window, windowtitle, -1);
		    while ((message = (struct IntuiMessage *)GetMsg(window->UserPort)) != 0)
			ReplyMsg(message);
		}
	    }
	    if (class == MENUPICK) {
		if (code != MENUNULL) {
		    whichmenu = MENUNUM(code);
		    whichitem = ITEMNUM(code);
		    switch (whichmenu) {
			case 0 : switch (whichitem) {
				    case 0: Request(&AboutRequesterStructure3, window); /* about */
					    while ((message = (struct IntuiMessage *)GetMsg(window->UserPort)) != 0)
						ReplyMsg(message);
					    break;
				    case 1: cont = FALSE; /* quit */
					    break;
				 }
				 break;
			case 1 : switch (whichitem) {
				    case 0: if (changefontsdir() == TRUE) {
						readfonts();
						if (selectfont() == 1) {
						    rethinkfont = TRUE;
						}
					    }
					    break;
				    case 1: if (fonts == NULL)
						readfonts();
					    if (selectfont() == 1)
						rethinkfont = TRUE;
					    break; /* font selection */
				 }
				 break;
			case 2 : if (definescreen() == TRUE) {
				     setupscreen();
				     rethinkfont = TRUE;
				 }
				 break;
		    }
		}
	    }
	}
    }

    cleanup(NULL);

}