/************************************************************************
Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

************************************************************************/

/* $XConsortium: osfonts.c,v 1.27 89/10/03 16:45:21 keith Exp $ */

#include <stdio.h>
#include <string.h>
#include "Xos.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>

#include "X.h"
#include "Xmd.h"
#include "Xproto.h"
#include "dixfontstr.h"
#include "fontstruct.h"
#include "osstruct.h"
#include "misc.h"
#include "opaque.h"
#include "dixstruct.h"

#include "fonttype.h"
#include "fontdir.h"
#include "msdos.h"

#ifndef MAXPATHLEN
#define MAXPATHLEN	255
#endif

extern FontPtr	ReadSNFFont();
extern Bool	ReadSNFProperties();
extern void	FreeSNFFont();

extern char * _near defaultFontPath;

static FontFile _near FindFontFile(FontPathPtr, char *, int, Bool, FontTable *);
static Bool _near SearchDirectory(int, char *, FontPathPtr, unsigned);
static int _near AddFontPathElement(FontPathPtr, char *, int, Bool);
static int _near ReadFontDir(char *, FontTable *, FontPathPtr);
static int _near ReadFontAlias(char *, Bool, FontTable *, FontPathPtr);
static FontPathPtr _near MakeFontPathRecord(unsigned size);
static int _near FindFileType(char *name);
static FILE * _near OpenFontFile(FontTable, char *);

/* maintained for benefit of GetFontPath */
static FontPathPtr searchList = (FontPathPtr)NULL;

void
FreeFontRecord(pFP)
    FontPathPtr pFP;
{
    int i;
    for (i=0; i<pFP->npaths; i++) {
        xfree(pFP->paths[i]);
    }
    xfree(pFP->paths);
    xfree(pFP->length);
    xfree(pFP->osPrivate);
    xfree(pFP);
}

static FontPathPtr _near
MakeFontPathRecord(size)
    unsigned	size;
{
    FontPathPtr fp;    

    fp = (FontPathPtr)xalloc(sizeof(FontPathRec));
    if (fp) {
	fp->npaths = 0;
	fp->size = size;
	fp->length = (int *)xalloc(size * sizeof(int));
	fp->paths = (char **)xalloc(size * sizeof(char *));
	fp->osPrivate = (pointer *)xalloc(size * sizeof(pointer));
	if (!fp->length || !fp->paths || !fp->osPrivate) {
	    xfree(fp->length);
	    xfree(fp->paths);
	    xfree(fp->osPrivate);
	    xfree(fp);
	    fp = (FontPathPtr)NULL;
	}
    }
    return fp;
}

static int _near
AddFontPathElement(path, element, length, fontDir)
    FontPathPtr path;
    char *element;
    int  length;
    Bool fontDir;
{
    int index = path->npaths;
    FontTable table;
    char *nelt;
    int status;
    char elename[MAXPATHLEN];

    if (fontDir)
    {
	/* Prepend the X11 environment variable to the path, if it
	 * doesn't start with '\\'.
	 */
	if (element[1] != ':' && X11env) {	/* No d: */
	    if (element[0] == '\\') {
		elename[0] = X11env[0];
		elename[1] = X11env[1];
		nelt = &elename[2];
	    } else {
		strcpy(elename, X11env);
		nelt = elename + strlen(elename);
		*nelt++ = '\\';
	    }
	    strncpy(nelt, element, length);
	    nelt[length] = '\0';
	    length += nelt - elename;
	    element = elename;
	}
	status = ReadFontDir(element, &table, path);
	if (status != Success)
	    return status;
    }
    nelt = (char *)xalloc(length + 1);
    if (!nelt)
	return BadAlloc;
    if (index >= path->size)
    {
	int size = path->size << 1;
	char **npaths;

	/* We must take care during the realloc of this first
	 * element or else we will lose the strings it pointed to!
	 */
	npaths = (char **)xrealloc(path->paths, size*sizeof(char *));
	if (npaths) {
	    path->length =
		(int *)xrealloc(path->length, size*sizeof(int));
	    path->osPrivate =
		(pointer *)xrealloc(path->osPrivate, size * sizeof(pointer));
	    path->paths = npaths;
	    path->size = size;
	}
	else
	{
	    int i;

	    for (i = 0; i < index; i++)
	       xfree(path->paths[i]);
	    path->npaths = 0;
	    path->paths = 0;
	}
	if (!path->length || !path->osPrivate || !path->paths)
	    return BadAlloc;
    }
    path->length[index] = length;
    path->paths[index] = nelt;
    strncpy(nelt, element, length);
    nelt[length] = '\0';
    path->osPrivate[index] = (pointer)table;
    path->npaths++;
    return Success;
}

static void
FreeFontPath(path)
    FontPathPtr path;
{
    int     i, j;
    FontPtr font;
    FontTable table;

    /* 
     * First all the back pointers for the outstanding fonts must be smashed
     * to NULL so that when the font is freed, the removal from the (now
     * freed) previous table can be skipped.
     */
    if (path) {
	for (i = 0; i < path->npaths; i++) {
	    table = (FontTable) path->osPrivate[i];
	    for (j = 0; j < table->file.used; j++) {
		if ((font = (FontPtr) table->file.ff[j].private) != NullFont)
		    font->osPrivate = NULL;
	    }
	    FreeFontTable (table);
	}
	FreeFontRecord (path);
    }
}

/*
 * Font paths are not smashed to lower case. (Note "/usr/lib/X11/fonts")
 *
 * Allow initial font path to have names separated by spaces tabs or commas
 */
int
SetDefaultFontPath(name)
    char *	name;
{
    char *start, *end;
    char dirName[MAXPATHLEN];
    FontPathPtr path;
    int status;

    path = MakeFontPathRecord(3);
    if (!path)
	return BadAlloc;
    end = name;
    for (;;) {
	start = end;
	while ((*start == ' ') || (*start == '\t') || (*start == ','))
	    start++;
	if (*start == '\0')
	    break;
	end = start;
	while ((*end != ' ') && (*end != '\t') && (*end != ',') &&
		(*end != '\0'))
	   end++;
	strncpy(dirName, start, end - start);
	dirName[end - start] = '\0';
	if (dirName[strlen(dirName) - 1] != '\\')
	    strcat(dirName, "\\");
	status = AddFontPathElement(path, dirName, strlen (dirName), True);
	if (status != Success)
	{
	    FreeFontPath(path);
	    return status;
	}
    }
    FreeFontPath(searchList);
    searchList = path;
    return Success;
}

int
SetFontPath(client, npaths, countedStrings)
    ClientPtr	client;
    unsigned	npaths;
    char *	countedStrings;
{
    int i;
    unsigned char * bufPtr = (unsigned char *)countedStrings;
    char dirName[MAXPATHLEN];
    unsigned int n;
    FontPathPtr path;
    int status;
    char *cp;

    if (npaths == 0)
	return SetDefaultFontPath(defaultFontPath); /* this frees old paths */

    path = MakeFontPathRecord(npaths);
    if (!path)
	return BadAlloc;
    cp = countedStrings;
    while (cp = strchr(cp, '/'))
	*cp++ = '\\';
    for (i=0; i<npaths; i++) {
	n = (unsigned int)(*bufPtr++);
	strncpy(dirName, (char *) bufPtr, (int) n);
	dirName[n] = '\0';
	if (dirName[n - 1] != '\\')
	    strcat(dirName, "\\");
	status = AddFontPathElement(path, dirName, strlen (dirName), True);
	if (status != Success)
	{
	    FreeFontPath(path);
	    client->errorValue = i;
	    return status;
	}
	bufPtr += n;
    }
    FreeFontPath(searchList);
    searchList = path;
    return Success;
}

FontPathPtr
GetFontPath()
{
    return(searchList);
}

static int _near
FindFileType(name)
    char *name;
{
    char *ext;

    ext = strrchr(name, '.');
    if (!ext)
	return -1;
    if (ext[1] == 'S' && ext[2] == 'N' && ext[3] == 'F')
	return 0;	/* ".snf" font */
    return -1;		/* that's all we do */
}

static int _near
ReadFontDir(directory, ptable, path)
    char	*directory;
    FontTable	*ptable;
    FontPathPtr path;
{
    char file_name[MAXPATHLEN];
    char font_name[MAXPATHLEN];
    char dir_file[MAXPATHLEN];
    FILE *file;
    int count, i, status;
    FontTable matchTable;
    FontTable table = NullTable;

    strcpy(dir_file, directory);
    if (directory[strlen(directory) - 1] != '\\')
 	strcat(dir_file, "\\");
    strcat(dir_file, FontDirFile);
    file = fopen(dir_file, "r");
    if (file)
    {
	count = fscanf(file, "%d\n", &i);
	if (count != 1) {
	    fclose(file);
	    return BadValue;
	}
	table = MakeFontTable(directory, count+10);
	for (;;) {
	    count = fscanf(file, "%s %[^\n]\n", file_name, font_name);
	    if (count == EOF)
		break;
	    if (count != 2) {
	        fclose(file);
		return BadValue;
	    }
	    if (!FindFontFile (path, font_name, 0, FALSE, &matchTable) &&
		(FindFileType (file_name) >= 0)) {
		i = AddFileEntry(table, file_name, False);
		if (i < 0) {
		    fclose(file);
		    return BadAlloc;
		}
		i = AddNameEntry(table, font_name, i);
		if (i <= 0) {
		    fclose(file);
		    return (i ? BadAlloc : BadValue);
		}
	    }
	}
	fclose(file);
    }
    else if (errno != ENOENT)
    {
	perror(dir_file);
	return BadValue;
    }
    status = ReadFontAlias(directory, FALSE, &table, path);
    if (status != Success)
    {
	FreeFontTable(table);
	return status;
    }
    if (!table)
	return BadValue;
    /*
     * At this point, no more entries will be made in file table. This means
     * that it will not be realloced and we can put pointers (rather than
     * indices into the table.
     */
    for (i = 0; i < table->name.used; i++)
	table->name.fn[i].u.ff = &table->file.ff[table->name.fn[i].u.index];

    *ptable = table;
    return Success;
}

/* 
 * Make each of the file names an automatic alias for each of the files.
 * This assumes that all file names are of the form <root>.<extension>.
 */

static Bool
AddFileNameAliases(table, path)
    FontTable table;
    FontPathPtr path;
{
    int i;
    Boolean found;
    FontTable	matchTable;
    char *cp;

    char copy[MAXPATHLEN];

    for (i = 0; i < table->file.used; i++) {
	if (table->file.ff[i].alias)
	    continue;
	strcpy(copy, table->file.ff[i].name);
	if (cp = strrchr(copy, '.'))
		*cp = '\0';
	CopyISOLatin1Lowered ((unsigned char *)copy, (unsigned char *)copy,
			      strlen (copy));
	(void)  FindNameInFontTable(table, copy, &found);
	if (!found && !FindFontFile (path, copy, 0, FALSE, &matchTable)) {
	    if (AddNameEntry(table, copy, i) < 0)
		return FALSE;
	}
    }
    return TRUE;
}

/*
 * parse the font.aliases file.  Format is:
 *
 * alias font-name
 *
 * To imbed white-space in an alias name, enclose it like "font name" 
 * in double quotes.  \ escapes and character, so
 * "font name \"With Double Quotes\" \\ and \\ back-slashes"
 * works just fine.
 */

/*
 * token types
 */

static int _near lexAlias (FILE *file, char **lexToken);
static int _near lexc (FILE *file);

# define NAME		0
# define NEWLINE	1
# define DONE		2
# define EALLOC		3

static int _near
ReadFontAlias(directory, isFile, ptable, path)
    char      *directory;
    Bool      isFile;
    FontTable *ptable;
    FontPathPtr path;
{
    char alias[MAXPATHLEN];
    char font_name[MAXPATHLEN];
    char alias_file[MAXPATHLEN];
    char buf[BUFSIZ];
    FILE *file;
    int i;
    FontTable matchTable;
    FontTable table;
    int	token;
    Bool found;
    char *lexToken;
    int status = Success;

    table = *ptable;
    strcpy(alias_file, directory);
    if (!isFile)
    {
	if (directory[strlen(directory) - 1] != '\\')
	    strcat(alias_file, "\\");
	strcat(alias_file, AliasFile);
    }
    file = fopen(alias_file, "r");
    if (!file) {
	if (errno != ENOENT) {
	    perror(alias_file);
	    return BadValue;
	}
	return Success;
    }
    setbuf (file, buf);
    if (!table)
	*ptable = table = MakeFontTable (directory, 10);
    if (!table) {
	fclose(file);
	return BadAlloc;
    }
    while (status == Success) {
	token = lexAlias (file, &lexToken);
	switch (token) {
	case NEWLINE:
	    break;
	case DONE:
	    fclose (file);
	    return Success;
	case EALLOC:
	    status = BadAlloc;
	    break;
	case NAME:
	    strcpy (alias, lexToken);
	    token = lexAlias (file, &lexToken);
	    switch (token) {
	    case NEWLINE:
		if (strcmp (alias, "FILE_NAMES_ALIASES"))
		    status = BadValue;
		else if (!AddFileNameAliases (table, path))
		    status = BadAlloc;
		break;
	    case DONE:
		status = BadValue;
		break;
	    case EALLOC:
		status = BadAlloc;
		break;
	    case NAME:
		CopyISOLatin1Lowered ((unsigned char *)alias,
				      (unsigned char *)alias,
				      strlen (alias));
		CopyISOLatin1Lowered ((unsigned char *)font_name,
				      (unsigned char *)lexToken,
				      strlen (lexToken));
		(void) FindNameInFontTable (table, alias, &found);
		if (!found &&
		    !FindFontFile (path, alias, 0, FALSE, &matchTable)) {
		    i = AddFileEntry(table, font_name, True);
		    if (i < 0) {
			status = BadAlloc;
		    } else {
			i = AddNameEntry(table, alias, i);
			if (i <= 0)
			    status = (i ? BadAlloc : BadValue);
		    }
		}
		break;
	    }
	}
    }
    fclose(file);
    return status;
}

# define QUOTE		0
# define WHITE		1
# define NORMAL		2
# define END		3
# define NL		4

static int	charClass;

static int _near
lexAlias (file, lexToken)
FILE	*file;
char	**lexToken;
{
	int		c;
	char		*t;
	enum state { Begin, Normal, Quoted } state;
	int		count;

	static char	*tokenBuf = (char *)NULL;
	static int	tokenSize = 0;

	t = tokenBuf;
	count = 0;
	state = Begin;
	for (;;) {
		if (count == tokenSize) {
		    int nsize;

		    nsize = tokenSize ? (tokenSize << 1) : 64;
		    tokenBuf = (char *)xrealloc(tokenBuf, nsize);
		    if (!tokenBuf) {
			tokenSize = 0;
			return EALLOC;
		    }
		    tokenSize = nsize;
		    t = tokenBuf + count;
		}
		c = lexc (file);
		switch (charClass) {
		case QUOTE:
			switch (state) {
			case Begin:
			case Normal:
				state = Quoted;
				break;
			case Quoted:
				state = Normal;
				break;
			}
			break;
		case WHITE:
			switch (state) {
			case Begin:
				continue;
			case Normal:
				*t = '\0';
				*lexToken = tokenBuf;
				return NAME;
			case Quoted:
				break;
			}
			/* fall through */
		case NORMAL:
			switch (state) {
			case Begin:
				state = Normal;
			}
			*t++ = c;
			++count;
			break;
		case END:
		case NL:
			switch (state) {
			case Begin:
				*lexToken = (char *)NULL;
				return charClass == END ? DONE : NEWLINE;
			default:
				*t = '\0';
				*lexToken = tokenBuf;
				ungetc (c, file);
				return NAME;
			}
		}
	}
}

static int _near
lexc (file)
FILE	*file;
{
	int	c;
	c = getc (file);
	switch (c) {
	case EOF:
		charClass = END;
		break;
	case '\\':
		c = getc (file);
		if (c == EOF)
			charClass = END;
		else
			charClass = NORMAL;
		break;
	case '"':
		charClass = QUOTE;
		break;
	case ' ':
	case '\t':
		charClass = WHITE;
		break;
	case '\n':
		charClass = NL;
		break;
	default:
		charClass = NORMAL;
		break;
	}
	return c;
}

static Bool _near
SearchDirectory(index, pat, fontList, limit)
    int index;
    char *pat;
    FontPathPtr fontList;
    unsigned limit;
{
    int i, res, len, head, tail;
    FontTable table;

    if (!searchList)
	return TRUE;
    table = (FontTable)searchList->osPrivate[index];
    i = SetupWildMatch(table, pat, (char *)NULL, &head, &tail, &len);
    while (i < table->name.used)
    {
	res = Match(table->name.fn[i].name, pat, head, tail, len);
	if (res > 0)
	{
	    if (AddFontPathElement(fontList, table->name.fn[i].name,
				   strlen(table->name.fn[i].name), False))
		return FALSE;
	    if (fontList->npaths >= limit)
		break;
	}
	else if (res < 0)
	    break;
	i++;
    }
    return TRUE;
}

/*******************************************************************
 *  ExpandFontNamePattern
 *
 *	Returns a FontPathPtr with at most max-names, of names of fonts
 *      matching
 *	the pattern.  The pattern should use the ASCII encoding, and
 *      upper/lower case does not matter.  In the pattern, the '?' character
 *	(octal value 77) will match any single character, and the character '*'
 *	(octal value 52) will match any number of characters.  The return
 *	names are in lower case.
 *
 *      Used only by protocol request ListFonts & ListFontsWithInfo
 *******************************************************************/

FontPathPtr
ExpandFontNamePattern(lenpat, countedPattern, maxNames)
    unsigned	lenpat;
    char	*countedPattern;
    unsigned	maxNames;
{
    char	*pattern;
    int		i;
    FontPathPtr	fpr;

    if (!searchList)
	return (FontPathPtr)NULL;
    /* random number, this is a guess, but it hardly matters. */
    fpr = MakeFontPathRecord ((unsigned) 100);
    if (!fpr)
	return (FontPathPtr)NULL;
    pattern = (char *)ALLOCATE_LOCAL (lenpat + 1);
    if (!pattern)
    {
	FreeFontRecord(fpr);
	return (FontPathPtr)NULL;
    }
    /*
     * make a pattern which is guaranteed NULL-terminated
     */
    CopyISOLatin1Lowered((unsigned char *)pattern,
			 (unsigned char *)countedPattern,
			 (int) lenpat);

    for ( i=0; i<searchList->npaths; i++)
    {
	if (!SearchDirectory(i, pattern, fpr, maxNames))
	{
	    FreeFontRecord(fpr);
	    return (FontPathPtr)NULL;
	}
	if (fpr->npaths >= maxNames)
	    break;
    }
    DEALLOCATE_LOCAL(pattern);
    return fpr;
}

/*
 * OS interface to reading font files. This is not called by the dix routines
 * but rather by any of the pseudo-os-independent font internalization
 * routines.
 */

int
FontFileRead(buf, itemsize, nitems, fid)
    char	*buf;
    unsigned	itemsize;
    unsigned	nitems;
    FID		fid;
{
    return fread( buf, (int) itemsize, (int) nitems, (FILE *)fid);
}

int
FontFileSkip(bytes, fid)
    unsigned	bytes;
    FID		fid;
{
    return fseek((FILE *) fid, (long) bytes, 1);
}

/*
 * When a font is unloaded (destroyed) it is necessary to remove the pointer
 * in the font table since it is no longer valid. This means that the
 * "obvious" technique of putting the FontPtr into the table when an aliased
 * font is loaded would mean that the table would have to be searched for
 * any matching entry. To avoid scanning all the tables when a font is FontPtr
 * destroyed, the Font has a back pointer to the FontFile (in the table) where
 * it was entered.
 *
 * So that aliases will not also keep a copy of the FontPtr in their FontTable
 * entries, a pointer to the "resolved" FontTable is actually kept and the
 * indirection is taken.
 *
 * A slight non-conformance to the protocol is possible here. If any
 * FontTable is for a file that does not load (i.e. was changed since the
 * font.dirs was created), then that font name will not work even though it
 * should because of wild carding or the use of a search path. The moral is
 * is that the font.dirs should be correct.
 *
 * To prevent circular aliases from crashing the server (due to the recursive
 * nature of FindFontFile) a limit of MAX_LINKS is put on the length of a
 * chain that will be followed.
 */

#define MAX_LINKS 20

static FontFile _near
FindFontFile(path, fontname, depth, followAliases, table)
    FontPathPtr path;
    char *	fontname;
    int		depth;
    Bool	followAliases;	
    FontTable	*table; 	/* returned */
{
    int     nameIndex, i;
    Bool found;
    FontFile ff, resolved;

    if (!path || (depth >= MAX_LINKS))
	return NullFontFile;
    for (i = 0; i < path->npaths; i++)
    {
	*table = (FontTable) path->osPrivate[i];
	nameIndex = FindNameInFontTable (*table, fontname, &found);
	if (!found)
	    continue;
	ff = (*table)->name.fn[nameIndex].u.ff;
	if (!ff->alias || !followAliases)
	    return ff;
	if (resolved = FindFontFile (path, ff->name, depth + 1, TRUE, table))
	{
	    ff->private = (Opaque) resolved;
	    return resolved;
	}
    }
    return NullFontFile;
}

static FILE * _near
OpenFontFile(table, name)
    FontTable table;
    char     *name;
{
    char    pathName[MAXPATHLEN];
    FILE   *fp;

    strcpy (pathName, table->directory);
    strcat (pathName, name);
    fp = fopen (pathName, "rb");
    if (!fp && errno != ENOENT)
	perror(pathName);
    return fp;
}

FontPtr 
FontFileLoad(fontname, length)
    char *	fontname;
    unsigned	length;
{
    FontPtr	font;
    char	fName[MAXPATHLEN];
    char	buf[BUFSIZ];
    FILE	* fp;
    FontTable	table;
    FontFile	ff;

    CopyISOLatin1Lowered((unsigned char *)fName, (unsigned char *)fontname,
			 (int) length);
    ff = FindFontFile (searchList, fName, 1, TRUE, &table);
    if (!ff)
	return NullFont;
    if (ff->private != NULL)
	return (FontPtr) ff->private;	/* already loaded */
    if ((fp = OpenFontFile(table, ff->name)) == NULL)
	return NullFont;
    setbuf (fp, buf);
    font = ReadSNFFont(fp);
    fclose(fp);
    if (font == NullFont)
	return NullFont;
    ff->private = (Opaque) font;
    font->refcnt = 0;
    font->fileType = 0;
    font->osPrivate = (pointer) ff;
    return font;
}

/*
 * This either returns an existing font, or if that can't be found,
 * then fills in the FontInfo and FontProp by reading from the disk.
 */

Bool
FontFilePropLoad(fontname, length, font, fi, props)
    char	*fontname;
    unsigned int length;
    FontInfoPtr fi;
    DIXFontPropPtr *props;	/* return */
    FontPtr	*font;		/* return */
{
    char	fName[MAXPATHLEN];
    FILE *	fp;
    FontTable	table;
    FontFile	ff;
    Bool	found;
    char	buf[BUFSIZ];

    CopyISOLatin1Lowered((unsigned char *)fName, (unsigned char *)fontname,
			 (int) length);
    ff = FindFontFile (searchList, fName, 1, TRUE, &table);
    if (!ff)
	return FALSE;
    if (ff->private != NULL) {
	*font = (FontPtr) ff->private;
	return TRUE;
    }
    *font = NullFont;
    if ((fp = OpenFontFile(table, ff->name)) == NULL)
	return FALSE;
    setbuf (fp, buf);
    found = ReadSNFProperties(fp, fi, props);
    fclose(fp);
    return found;
}

void FontUnload(font)
    FontPtr font;
{
    FontFile ff;
    if ((ff = (FontFile)font->osPrivate) != NULL)
	ff->private = NULL;
    FreeSNFFont(font);
}
