/*
 * X11 physical font objects
 *
 * Copyright 1997 Alex Korobka
 *
 * TODO: Mapping algorithm tweaks, FO_SYNTH_... flags (ExtTextOut() will
 *	 have to be changed for that), dynamic font loading (FreeType).
 */

#include "config.h"

#ifndef X_DISPLAY_MISSING
#include <X11/Xatom.h>
#include "ts_xlib.h"
#include "x11font.h"
#endif /* !defined(X_DISPLAY_MISSING) */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <math.h>
#include <assert.h>
#include "winuser.h"
#include "heap.h"
#include "options.h"
#include "font.h"
#include "debugtools.h"
#include "ldt.h"

DEFAULT_DEBUG_CHANNEL(font)

#ifndef X_DISPLAY_MISSING

#define X_PFONT_MAGIC		(0xFADE0000)
#define X_FMC_MAGIC		(0x0000CAFE)

#define MAX_FONT_FAMILIES	1024
#define MAX_LFD_LENGTH		256

#define MAX_FONT_SIZE           1000    /* Max size in pixels */

#define REMOVE_SUBSETS		1
#define UNMARK_SUBSETS		0

#define DEF_SCALABLE_HEIGHT	24
#define DEF_SCALABLE_DP		240

#define FF_FAMILY       (FF_MODERN | FF_SWISS | FF_ROMAN | FF_DECORATIVE | FF_SCRIPT)

typedef struct __fontAlias
{
  LPSTR			faTypeFace;
  LPSTR			faAlias;
  struct __fontAlias*	next;
} fontAlias;

typedef struct
{
  LPSTR                 fatResource;
  LPSTR                 fatAlias;
} aliasTemplate;

/* Font alias table - these 2 aliases are always present */
/* CHANGING THE FIRST TWO WILL BREAK XFONT_BuildDefaultAliases! */

static fontAlias __aliasTable[2] = { 
			{ NULL, "Helv", &__aliasTable[1] },
			{ NULL, "Tms Rmn", NULL } 
			};

static fontAlias *aliasTable = __aliasTable;

/* Optional built-in aliases, they are installed only when X
 * cannot supply us with original MS fonts */
/* CHANGING THE FIRST TWO WILL BREAK XFONT_BuildDefaultAliases! */

static int	     faTemplateNum = 4;
static aliasTemplate faTemplate[4] = {
			  { NULL, "MS Sans Serif" },
			  { NULL, "MS Serif" },
			  { "-adobe-times-", "Times New Roman" },
			  { "-adobe-helvetica-", "Arial" }
			  };

UINT16			XTextCaps = TC_OP_CHARACTER | TC_OP_STROKE |
TC_CP_STROKE | TC_CR_ANY |
				    TC_SA_DOUBLE | TC_SA_INTEGER | TC_SA_CONTIN |
			 	    TC_UA_ABLE | TC_SO_ABLE | TC_RA_ABLE;

			/* X11R6 adds TC_SF_X_YINDEP, maybe more... */

static const char*	INIWinePrefix = "/.wine";
static const char*	INIFontMetrics = "/.cachedmetrics.";
static const char*	INIFontSection = "fonts";
static const char*	INIAliasSection = "Alias";
static const char*	INIIgnoreSection = "Ignore";
static const char*	INIDefault = "Default";
static const char*	INIDefaultFixed = "DefaultFixed";
static const char*	INIResolution = "Resolution";
static const char*	INIGlobalMetrics = "FontMetrics";
static const char*	INIDefaultSerif = "DefaultSerif";
static const char*	INIDefaultSansSerif = "DefaultSansSerif";

static const char*	LFDSeparator = "*-";

/* suffix tables, must be less than 254 entries long */

static LPSTR	suffx_iso[] = { "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", 
				  "-9", "-10", "-11", "-12", "-13", "-14", "-15", NULL };
static LPSTR	suffx_iso646[] = { "-irv", NULL };
static LPSTR	suffx_microsoft[] = { "-cp1252", "-cp1251", "-cp1250", "-cp1253", "-cp1254", "-cp1255",
				      "-cp1256", "-cp1257", "-fontspecific", "-symbol", NULL };
static LPSTR    suffx_windows[] = { "-1252", "-1251", "-1250", "-1253", "-1254", 
                                    "-1256", "-1257", NULL };
static LPSTR	suffx_viscii[] = { "-1", NULL };
static LPSTR	suffx_ansi[] = { "-0", NULL };
static LPSTR	suffx_null[] = { NULL };

/* charset mapping tables, have to have the same number of entries as corresponding suffix tables */

static BYTE	chset_iso8859[] = { ANSI_CHARSET, EE_CHARSET, ISO3_CHARSET, ISO4_CHARSET, RUSSIAN_CHARSET,
				    ARABIC_CHARSET, GREEK_CHARSET, HEBREW_CHARSET, TURKISH_CHARSET, BALTIC_CHARSET,
				    THAI_CHARSET, SYMBOL_CHARSET, SYMBOL_CHARSET, SYMBOL_CHARSET, ANSI_CHARSET, 
				    SYMBOL_CHARSET };
static BYTE	chset_iso646[] = { ANSI_CHARSET, SYMBOL_CHARSET };
static BYTE	chset_microsoft[] = { ANSI_CHARSET, RUSSIAN_CHARSET, EE_CHARSET, GREEK_CHARSET, TURKISH_CHARSET,
				      HEBREW_CHARSET, ARABIC_CHARSET, BALTIC_CHARSET, SYMBOL_CHARSET, SYMBOL_CHARSET, 
				      SYMBOL_CHARSET };
static BYTE	chset_ansi[] = { ANSI_CHARSET, ANSI_CHARSET };
static BYTE     chset_windows[] = { ANSI_CHARSET, RUSSIAN_CHARSET, EE_CHARSET, GREEK_CHARSET, TURKISH_CHARSET,
                                    HEBREW_CHARSET, ARABIC_CHARSET, BALTIC_CHARSET, BALTIC_CHARSET}; 
static BYTE	chset_tcvn[] = { TCVN_CHARSET, TCVN_CHARSET };
static BYTE	chset_tis620[] = { THAI_CHARSET, THAI_CHARSET };
static BYTE	chset_fontspecific[] = { SYMBOL_CHARSET };
static BYTE	chset_viscii[] = { VISCII_CHARSET, VISCII_CHARSET };

typedef struct __fet
{
  LPSTR		prefix;
  LPSTR		(*psuffix)[];
  BYTE		(*pcharset)[];
  struct __fet* next;
} fontEncodingTemplate;

/* Note: we can attach additional encoding mappings to the end
 *       of this table at runtime.
 */

static fontEncodingTemplate __fETTable[10] = {
			{ "iso8859", &suffx_iso, &chset_iso8859, &__fETTable[1] },
			{ "iso646.1991", &suffx_iso646, &chset_iso646,  &__fETTable[2] },
			{ "microsoft",  &suffx_microsoft, &chset_microsoft, &__fETTable[3] },
			{ "ansi", &suffx_ansi, &chset_ansi, &__fETTable[4] },
			{ "ascii", &suffx_ansi, &chset_ansi, &__fETTable[5] },
			{ "fontspecific", &suffx_null, &chset_fontspecific, &__fETTable[6] },
			{ "windows", &suffx_windows, &chset_windows, &__fETTable[7] },
			{ "tcvn", &suffx_ansi, &chset_tcvn, &__fETTable[8] },
			{ "tis620.2533", &suffx_ansi, &chset_tis620, &__fETTable[9] },
			{ "viscii1.1", &suffx_viscii, &chset_viscii, NULL }
		};
static fontEncodingTemplate* fETTable = __fETTable;

static unsigned		DefResolution = 0;

static CRITICAL_SECTION crtsc_fonts_X11;

static fontResource*	fontList = NULL;
static fontObject*      fontCache = NULL;		/* array */
static int		fontCacheSize = FONTCACHE;
static int		fontLF = -1, fontMRU = -1;	/* last free, most recently used */

#define __PFONT(pFont)     ( fontCache + ((UINT)(pFont) & 0x0000FFFF) )
#define CHECK_PFONT(pFont) ( (((UINT)(pFont) & 0xFFFF0000) == X_PFONT_MAGIC) &&\
			     (((UINT)(pFont) & 0x0000FFFF) < fontCacheSize) )

static INT XFONT_IsSubset(fontInfo*, fontInfo*);
static void  XFONT_CheckFIList(fontResource*, fontInfo*, int subset_action);
static void  XFONT_GrowFreeList(int start, int end);
static void XFONT_RemoveFontResource(fontResource** ppfr);


static Atom RAW_ASCENT;
static Atom RAW_DESCENT;

/***********************************************************************
 *           Helper macros from X distribution
 */

#define CI_NONEXISTCHAR(cs) (((cs)->width == 0) && \
			     (((cs)->rbearing|(cs)->lbearing| \
			       (cs)->ascent|(cs)->descent) == 0))

#define CI_GET_CHAR_INFO(fs,col,def,cs) \
{ \
    cs = def; \
    if (col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
	if (fs->per_char == NULL) { \
	    cs = &fs->min_bounds; \
	} else { \
	    cs = &fs->per_char[(col - fs->min_char_or_byte2)]; \
	    if (CI_NONEXISTCHAR(cs)) cs = def; \
	} \
    } \
}

#define CI_GET_DEFAULT_INFO(fs,cs) \
  CI_GET_CHAR_INFO(fs, fs->default_char, NULL, cs)

/***********************************************************************
 *           Checksums
 */
static UINT16   __lfCheckSum( LPLOGFONT16 plf )
{
    CHAR        font[LF_FACESIZE];
    UINT16      checksum = 0;
    UINT16      i;

#define ptr ((UINT16*)plf)
   for( i = 0; i < 9; i++ ) checksum ^= *ptr++;
#undef ptr
   i = 0;
#define ptr ((CHAR*)plf)
   do { font[i++] = tolower(*ptr++); } while (( i < LF_FACESIZE) && (*ptr) && (*ptr!=' '));
   for( ptr = font, i >>= 1; i > 0; i-- ) 
#undef ptr
#define ptr ((UINT16*)plf)
        checksum ^= *ptr++;
#undef ptr
   return checksum;
}

static UINT16   __genericCheckSum( const void *ptr, int size )
{
   unsigned int checksum = 0;
   const char *p = (const char *)ptr;
   while (size-- > 0)
     checksum ^= (checksum << 3) + (checksum >> 29) + *p++;

   return checksum & 0xffff;
}

/*************************************************************************
 *           LFD parse/compose routines
 */
static char* LFD_Advance(LPSTR lpFont, UINT16 uParmsNo)
{
  int 	j = 0;
  char*	lpch = lpFont;

  for( ; j < uParmsNo && *lpch; lpch++ ) if( *lpch == LFDSeparator[1] ) j++;
  return lpch;
}

static void LFD_GetWeight( fontInfo* fi, LPSTR lpStr, int j)
{
  if( j == 1 && *lpStr == '0' )
      fi->fi_flags |= FI_POLYWEIGHT;
  else if( j == 4 )
       {
         if( !strncasecmp( "bold", lpStr, 4) )
           fi->df.dfWeight = FW_BOLD; 
	 else if( !strncasecmp( "demi", lpStr, 4) )
	 {
	   fi->fi_flags |= FI_FW_DEMI;
	   fi->df.dfWeight = FW_DEMIBOLD;
	 }
	 else if( !strncasecmp( "book", lpStr, 4) )
	 {
	   fi->fi_flags |= FI_FW_BOOK;
	   fi->df.dfWeight = FW_REGULAR;
	 }
       }
  else if( j == 5 )
       {
         if( !strncasecmp( "light", lpStr, 5) )
	   fi->df.dfWeight = FW_LIGHT;
         else if( !strncasecmp( "black", lpStr, 5) )
	   fi->df.dfWeight = FW_BLACK;
       }
  else if( j == 6 && !strncasecmp( "medium", lpStr, 6) )
      fi->df.dfWeight = FW_REGULAR; 
  else if( j == 8 && !strncasecmp( "demibold", lpStr, 8) )
      fi->df.dfWeight = FW_DEMIBOLD; 
  else
      fi->df.dfWeight = FW_DONTCARE; /* FIXME: try to get something
				      * from the weight property */
}

static int LFD_GetSlant( fontInfo* fi, LPSTR lpStr, int l)
{
    if( l == 1 )
    {
	switch( tolower( *lpStr ) )
	{
	    case '0':  fi->fi_flags |= FI_POLYSLANT;	/* haven't seen this one yet */
	    default:
	    case 'r':  fi->df.dfItalic = 0;
		       break;
	    case 'o':
		       fi->fi_flags |= FI_OBLIQUE;
	    case 'i':  fi->df.dfItalic = 1;
		       break;
	}
	return 0;
    }
    return 1;
}

/*************************************************************************
 *           LFD_InitFontInfo
 *
 * INIT ONLY
 *
 * Fill in some fields in the fontInfo struct.
 */
static int LFD_InitFontInfo( fontInfo* fi, LPSTR lpstr )
{
   LPSTR	lpch;
   int    	i, j, dec_style_check, scalability;
   UINT16 	tmp[3];
   fontEncodingTemplate* boba;

   memset(fi, 0, sizeof(fontInfo) );

/* weight name - */
   lpch = LFD_Advance( lpstr, 1);
   if( !*lpch ) return FALSE;
   j = lpch - lpstr - 1;
   LFD_GetWeight( fi, lpstr, j );

/* slant - */
   lpch = LFD_Advance( lpstr = lpch, 1);
   if( !*lpch ) return FALSE;
   j = lpch - lpstr - 1;
   dec_style_check = LFD_GetSlant( fi, lpstr, j );

/* width name - */
   lpch = LFD_Advance( lpstr = lpch, 1);
   if( !*lpch ) return FALSE;
   if( strncasecmp( "normal", lpstr, 6) )	/* XXX 'narrow', 'condensed', etc... */
       dec_style_check = TRUE;
   else
       fi->fi_flags |= FI_NORMAL;

/* style - */
   lpch = LFD_Advance( lpstr = lpch, 1);
   if( !*lpch ) return FALSE;
   j = lpch - lpstr - 1;
   if( j > 3 )	/* find out is there "sans" or "script" */
   {
	j = 0;
	*(lpch - 1) = '\0';

	if( strstr(lpstr, "sans") ) 
	{ 
	    fi->df.dfPitchAndFamily |= FF_SWISS; 
	    j = 1; 
	}
	if( strstr(lpstr, "script") ) 
	{ 
	    fi->df.dfPitchAndFamily |= FF_SCRIPT; 
	    j = 1; 
	}
	if( !j && dec_style_check ) 
	    fi->df.dfPitchAndFamily |= FF_DECORATIVE;
	*(lpch - 1) = LFDSeparator[1];
   }

/* pixel height, decipoint height, and res_x */

   for( i = scalability = 0; i < 3; i++ )
   {
     lpch = LFD_Advance( lpstr = lpch, 1);
     if( !*lpch ) return FALSE;
     if( lpch - lpstr == 1 || lpch - lpstr > 4 ) return FALSE; /* ridiculous */

    *(lpch - 1) = '\0';
     if( !(tmp[i] = atoi(lpstr)) ) scalability++;
    *(lpch - 1) = LFDSeparator[1];
   }
   if( scalability == 3 ) 	/* Type1 */
   {
     fi->fi_flags |= FI_SCALABLE;
     fi->lfd_height = DEF_SCALABLE_HEIGHT; fi->lfd_decipoints = DEF_SCALABLE_DP;
     fi->lfd_resolution = DefResolution;
   }
   else if( scalability == 0 )	/* Bitmap */
   {
     fi->lfd_height = tmp[0]; fi->lfd_decipoints = tmp[1]; 
     fi->lfd_resolution = tmp[2];
   }
   else return FALSE; /* #$%^!!! X11R6 mutant garbage */

/* res_y - skip, spacing - */
   lpstr = LFD_Advance( lpch, 1);
   switch( *lpstr )
   {
     case '\0': return FALSE;

     case 'p': fi->fi_flags |= FI_VARIABLEPITCH;
	       break;
     case 'c': fi->df.dfPitchAndFamily |= FF_MODERN;
	       fi->fi_flags |= FI_FIXEDEX;
	       /* fall through */
     case 'm': fi->fi_flags |= FI_FIXEDPITCH;
	       break;
     default:
	       fi->df.dfPitchAndFamily |= DEFAULT_PITCH | FF_DONTCARE;
   }
   lpstr = LFD_Advance(lpstr, 1);
   if( !*lpstr ) return FALSE;

/* average width - */
   lpch = LFD_Advance( lpstr, 1);
   if( !*lpch ) return FALSE;
   if( lpch - lpstr == 1 || lpch - lpstr > 4 ) return FALSE; /* ridiculous */
  *(lpch - 1) = '\0'; 
   if( !(fi->lfd_width = atoi(lpstr)) && !scalability ) return FALSE;
  *(lpch - 1) = LFDSeparator[1];

/* charset registry, charset encoding - */
   if( strstr(lpch, "jisx") || 
       strstr(lpch, "ksc") || 
       strstr(lpch, "gb2312") ||
       strstr(lpch, "big5") ||
       strstr(lpch, "unicode") ) return FALSE;	/* 2-byte stuff */

   fi->df.dfCharSet = ANSI_CHARSET;

   for( i = 0, boba = fETTable; boba; boba = boba->next, i++ )
   {
	tmp[0] = strlen( boba->prefix );
	if( !strncasecmp( lpch, boba->prefix, tmp[0] ) )
	{
	    if( lpch[tmp[0]] == '-' )
	    {
		lpstr = lpch + tmp[0];
		for( j = 0; (*(boba->psuffix))[j]; j++ )
		{
		     tmp[1] = strlen( (*(boba->psuffix))[j] );
		     if( !strncasecmp( lpstr, (*(boba->psuffix))[j], tmp[1] ) )
		     {
			 fi->df.dfCharSet = (*(boba->pcharset))[j];
			 goto done;
		     }
		}
		/* Note : LFD_ComposeLFD will produce 'prefix-*' encoding     *
		 *			if (*(boba->psuffix))[j] is NULL here */
	    }
	    else
	    {
		for( j = 0; (*(boba->psuffix))[j] ; j++ );
		fi->df.dfCharSet = (*(boba->pcharset))[j];
		j = lpch[tmp[0]] ? 254 : 255;
	    }
	}
   }
done:
   if( !boba ) return FALSE;

   /* i - index into fETTable
    * j - index into suffix array for fETTable[i]
    *     except:
    *     254 - weird suffix (i.e. no '-' right after prefix )
    *     255 - no suffix at all.
    */

   fi->fi_encoding = 256 * (UINT16)i + (UINT16)j;

   return TRUE;					
}


/*************************************************************************
 *           LFD_ComposeLFD
 *
 * Note: uRelax is a treatment not a cure. Font mapping algorithm
 *       should be bulletproof enough to allow us to avoid hacks like
 *	 if( uRelax == 200 ) even despite LFD being so braindead.
 */
static BOOL  LFD_ComposeLFD( fontObject* fo, 
			       INT height, LPSTR lpLFD, UINT uRelax )
{
   fontEncodingTemplate* boba;
   int		i, h, w, ch, point = 0;
   char*	lpch; 
   char		lpEncoding[64];
   char         h_string[64], point_string[64];

   *(lpLFD+MAX_LFD_LENGTH-1)=0;
   lstrcpyA( lpLFD, fo->fr->resource );

/* add weight */
   switch( fo->fi->df.dfWeight )
   {
	case FW_BOLD:
		strcat( lpLFD, "bold" ); break;
	case FW_REGULAR:
		if( fo->fi->fi_flags & FI_FW_BOOK )
		    strcat( lpLFD, "book" );
		else
		    strcat( lpLFD, "medium" );
		break;
	case FW_DEMIBOLD:
		strcat( lpLFD, "demi" );
		if( !( fo->fi->fi_flags & FI_FW_DEMI) )
		     strcat ( lpLFD, "bold" );
		break;
	case FW_BLACK:
		strcat( lpLFD, "black" ); break;
	case FW_LIGHT:
		strcat( lpLFD, "light" ); break;
	default:
		strcat( lpLFD, "*" );
   }

/* add slant */
   if( fo->fi->df.dfItalic )
       if( fo->fi->fi_flags & FI_OBLIQUE )
	   strcat( lpLFD, "-o" );
       else
	   strcat( lpLFD, "-i" );
   else 
       strcat( lpLFD, (uRelax < 2) ? "-r" : "-*" );

/* add width style and skip serifs */
   if( fo->fi->fi_flags & FI_NORMAL )
       strcat( lpLFD, "-normal-*-");
   else
       strcat( lpLFD, "-*-*-" );

/* add pixelheight, pointheight, and resolution 
 *
 * FIXME: fill in lpXForm and lpPixmap for rotated fonts
 */
   if( fo->fo_flags & FO_SYNTH_HEIGHT ) h = fo->fi->lfd_height;
   else h = (fo->fi->lfd_height * height) / fo->fi->df.dfPixHeight;

   if( XTextCaps & TC_SF_X_YINDEP ) 
   {
	if( fo->lf.lfWidth && !(fo->fo_flags & FO_SYNTH_WIDTH) )
	    point = (fo->fi->lfd_decipoints * fo->lf.lfWidth) / fo->fi->df.dfAvgWidth;
	else 
	if( fo->fi->fi_flags & FI_SCALABLE ) /* adjust h/w ratio */
	    point = h * 72 * 10 / fo->fi->lfd_resolution;
   }

   /* handle rotated fonts */
   if (fo->lf.lfEscapement) {
     /* escapement is in tenths of degrees, theta is in radians */
     double theta = M_PI*fo->lf.lfEscapement/1800.;  
     double h_matrix[4];
     double point_matrix[4];
     char *s;
     h_matrix[0] = h*cos(theta);
     h_matrix[1] = h*sin(theta);
     h_matrix[2] = -h*sin(theta);
     h_matrix[3] = h*cos(theta);
     point_matrix[0] = point*cos(theta);
     point_matrix[1] = point*sin(theta);
     point_matrix[2] = -point*sin(theta);
     point_matrix[3] = point*cos(theta);
     sprintf(h_string, "[%+f%+f%+f%+f]", h_matrix[0], h_matrix[1], h_matrix[2], h_matrix[3]);
     sprintf(point_string, "[%+f%+f%+f%+f]", point_matrix[0], point_matrix[1], point_matrix[2], point_matrix[3]);
     while ((s = strchr(h_string, '-'))) *s='~';
     while ((s = strchr(point_string, '-'))) *s='~';
   } else {
     sprintf(h_string, "%d", h);
     sprintf(point_string, "%d", point);
   }


/* spacing and width */

   if( fo->fi->fi_flags & FI_FIXEDPITCH ) 
       w = ( fo->fi->fi_flags & FI_FIXEDEX ) ? 'c' : 'm';
   else 
       w = ( fo->fi->fi_flags & FI_VARIABLEPITCH ) ? 'p' : LFDSeparator[0]; 

/* encoding */

   i = fo->fi->fi_encoding >> 8;
   for( boba = fETTable; i; i--, boba = boba->next );

   strcpy( lpEncoding, boba->prefix );

   i = fo->fi->fi_encoding & 255;
   switch( i )
   {
	case 254: strcat( lpEncoding, "*" );
		  break;
	default:
		  
		  if( (*(boba->psuffix))[i] )
		      strcat( lpEncoding, (*(boba->psuffix))[i] );
		  else
		      strcat( lpEncoding, "-*" );
		  /* fall through */

	case 255: /* no suffix */
	          break;
   }

   lpch = lpLFD + lstrlenA(lpLFD);
   ch = (fo->fi->fi_flags & FI_SCALABLE) ? '0' : LFDSeparator[0];

   switch( uRelax )
   {
       /* RealizeFont() will call us repeatedly with increasing uRelax 
	* until XLoadFont() succeeds. */

       case 0: 
	    if( point )
	    {
	        sprintf( lpch, "%s-%s-%i-%c-%c-*-%s", h_string, 
			 point_string, 
			 fo->fi->lfd_resolution, ch, w, lpEncoding );
	        break;
	    }
	    /* fall through */

       case 1: 
       case 2: /* 2 will have replaced an 'r' in slant by '*' */
	    sprintf( lpch, "%s-*-%i-%c-%c-*-%s", h_string, 
			fo->fi->lfd_resolution, ch, w, lpEncoding );
	    break;

       case 3:
	    sprintf( lpch, "%s-*-%i-%c-*-*-%s",
			h_string, fo->fi->lfd_resolution, ch, lpEncoding );
	    break;

       case 4:
	    sprintf( lpch, "%i-*-%i-%c-*-*-%s", fo->fi->lfd_height,
			fo->fi->lfd_resolution, ch, lpEncoding );
	    break;

       case 5:
	    sprintf( lpch, "%i-*-*-*-*-*-%s", fo->fi->lfd_height, lpEncoding );
	    break;

       default:
	    sprintf( lpch, "%i-*-*-*-*-*-*-*", fo->fi->lfd_height);
	    break;

       /* to avoid an infinite loop; those will allways match */
       case 200:
            sprintf( lpLFD, "-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-1" );
            break;
       case 201:
            sprintf( lpLFD, "-*-*-*-*-*-*-*-*-*-*-*-*-*-*" );
            break;
   }

   TRACE("\tLFD(uRelax=%d): %s\n", uRelax, lpLFD );
   assert(*(lpLFD+MAX_LFD_LENGTH-1)==0); /* check if overwrittem */
   return TRUE;
}


/***********************************************************************
 *		X Font Resources
 *
 * font info		- http://www.microsoft.com/kb/articles/q65/1/23.htm
 * Windows font metrics	- http://www.microsoft.com/kb/articles/q32/6/67.htm
 */
static BOOL XFONT_GetLeading( LPIFONTINFO16 pFI, XFontStruct* x_fs, INT*
       pIL, INT* pEL, XFONTTRANS *XFT )
{
    unsigned long height;
    unsigned min = (unsigned char)pFI->dfFirstChar;
    unsigned max = (unsigned char)pFI->dfLastChar;
    BOOL bHaveCapHeight = (pFI->dfCharSet == ANSI_CHARSET && 'X' >= min && 'X' <= max );

    if( pEL ) *pEL = 0;

    if(XFT) {
        Atom RAW_CAP_HEIGHT = TSXInternAtom(display, "RAW_CAP_HEIGHT", TRUE);
	if(TSXGetFontProperty(x_fs, RAW_CAP_HEIGHT, &height))
	    *pIL = XFT->ascent - 
                            (INT)(XFT->pixelsize / 1000.0 * height);
	else
	    *pIL = 0;
	return bHaveCapHeight && x_fs->per_char;
    }
       
    if( TSXGetFontProperty(x_fs, XA_CAP_HEIGHT, &height) == FALSE )
    {
        if( x_fs->per_char )
	    if( bHaveCapHeight )
		    height = x_fs->per_char['X' - min].ascent;
	    else
		if (x_fs->ascent >= x_fs->max_bounds.ascent)
		    height = x_fs->max_bounds.ascent;
		else
		{
		    height = x_fs->ascent;
		    if( pEL )
			*pEL = x_fs->max_bounds.ascent - height;
		}
	else 
	    height = x_fs->min_bounds.ascent;
    }

   *pIL = x_fs->ascent - height;
    return (bHaveCapHeight && x_fs->per_char);
}

static INT XFONT_GetAvgCharWidth( LPIFONTINFO16 pFI, XFontStruct* x_fs,
				    XFONTTRANS *XFT)
{
    unsigned min = (unsigned char)pFI->dfFirstChar;
    unsigned max = (unsigned char)pFI->dfLastChar;

    if( x_fs->per_char )
    {
	int  width, chars, j;
	for( j = 0, width = 0, chars = 0, max -= min; j <= max; j++ )
            if( !CI_NONEXISTCHAR(x_fs->per_char + j) )
            {
	        if(!XFT)
		    width += x_fs->per_char[j].width;
		else
		    width += x_fs->per_char[j].attributes * 
		      XFT->pixelsize / 1000.0;
                chars++;
            }
	return (width / chars);
    }
    /* uniform width */
    return x_fs->min_bounds.width;
}

static INT XFONT_GetMaxCharWidth(fontObject *pfo)
{
    unsigned min = (unsigned char)pfo->fs->min_char_or_byte2;
    unsigned max = (unsigned char)pfo->fs->max_char_or_byte2;

    if(!pfo->lpX11Trans)
        return abs(pfo->fs->max_bounds.width);

    if( pfo->fs->per_char )
    {
	int  maxwidth, j;
	for( j = 0, maxwidth = 0, max -= min; j <= max; j++ )
            if( !CI_NONEXISTCHAR(pfo->fs->per_char + j) )
	        if(maxwidth < pfo->fs->per_char[j].attributes)
		    maxwidth = pfo->fs->per_char[j].attributes;

	maxwidth *= pfo->lpX11Trans->pixelsize / 1000.0;
	return maxwidth;
    }
    return pfo->foAvgCharWidth;
}

/***********************************************************************
 *              XFONT_SetFontMetric
 *
 * INIT ONLY
 *
 * Initializes IFONTINFO16. dfHorizRes and dfVertRes must be already set.
 */
static void XFONT_SetFontMetric(fontInfo* fi, fontResource* fr, XFontStruct* xfs)
{
    unsigned	 min, max;
    INT	 el, il;

    fi->df.dfFirstChar = (BYTE)(min = xfs->min_char_or_byte2);
    fi->df.dfLastChar = (BYTE)(max = xfs->max_char_or_byte2);

    fi->df.dfDefaultChar = (BYTE)xfs->default_char;
    fi->df.dfBreakChar = (BYTE)(( ' ' < min || ' ' > max) ? xfs->default_char: ' ');

    fi->df.dfPixHeight = (INT16)((fi->df.dfAscent = (INT16)xfs->ascent) + xfs->descent);
    fi->df.dfPixWidth = (xfs->per_char) ? 0 : xfs->min_bounds.width;
    fi->df.dfMaxWidth = (INT16)abs(xfs->max_bounds.width);

    if( XFONT_GetLeading( &fi->df, xfs, &il, &el, NULL ) )
        fi->df.dfAvgWidth = (INT16)xfs->per_char['X' - min].width;
    else
        fi->df.dfAvgWidth = (INT16)XFONT_GetAvgCharWidth( &fi->df, xfs, NULL);

    fi->df.dfInternalLeading = (INT16)il;
    fi->df.dfExternalLeading = (INT16)el;

    fi->df.dfPoints = (INT16)(((INT)(fi->df.dfPixHeight - 
	       fi->df.dfInternalLeading) * 72 + (fi->df.dfVertRes >> 1)) / fi->df.dfVertRes);

    if( xfs->min_bounds.width != xfs->max_bounds.width )
        fi->df.dfPitchAndFamily |= TMPF_FIXED_PITCH; /* au contraire! */
    if( fi->fi_flags & FI_SCALABLE ) 
    {
	fi->df.dfType = DEVICE_FONTTYPE;
        fi->df.dfPitchAndFamily |= TMPF_DEVICE;
    } 
    else if( fi->fi_flags & FI_TRUETYPE )
	fi->df.dfType = TRUETYPE_FONTTYPE;
    else
	fi->df.dfType = RASTER_FONTTYPE;

    fi->df.dfFace = fr->lfFaceName;
}

/***********************************************************************
 *              XFONT_GetTextMetric
 *
 * GetTextMetrics() back end.
 */
static void XFONT_GetTextMetric( fontObject* pfo, LPTEXTMETRICA pTM )
{
    LPIFONTINFO16 pdf = &pfo->fi->df;

    if( ! pfo->lpX11Trans ) {
      pTM->tmAscent = pfo->fs->ascent;
      pTM->tmDescent = pfo->fs->descent;
    } else {
      pTM->tmAscent = pfo->lpX11Trans->ascent;
      pTM->tmDescent = pfo->lpX11Trans->descent;
    }
    pTM->tmAscent *= pfo->rescale;
    pTM->tmDescent *= pfo->rescale;

    pTM->tmHeight = pTM->tmAscent + pTM->tmDescent;

    pTM->tmAveCharWidth = pfo->foAvgCharWidth * pfo->rescale;
    pTM->tmMaxCharWidth = pfo->foMaxCharWidth * pfo->rescale;

    pTM->tmInternalLeading = pfo->foInternalLeading * pfo->rescale;
    pTM->tmExternalLeading = pdf->dfExternalLeading * pfo->rescale;

    pTM->tmStruckOut = (pfo->fo_flags & FO_SYNTH_STRIKEOUT )
			? 1 : pdf->dfStrikeOut;
    pTM->tmUnderlined = (pfo->fo_flags & FO_SYNTH_UNDERLINE )
			? 1 : pdf->dfUnderline;

    pTM->tmOverhang = 0;
    if( pfo->fo_flags & FO_SYNTH_ITALIC ) 
    {
	pTM->tmOverhang += pTM->tmHeight/3;
	pTM->tmItalic = 1;
    } else 
	pTM->tmItalic = pdf->dfItalic;

    pTM->tmWeight = pdf->dfWeight;
    if( pfo->fo_flags & FO_SYNTH_BOLD ) 
    {
	pTM->tmOverhang++; 
	pTM->tmWeight += 100;
    } 

    *(INT*)&pTM->tmFirstChar = *(INT*)&pdf->dfFirstChar;

    pTM->tmCharSet = pdf->dfCharSet;
    pTM->tmPitchAndFamily = pdf->dfPitchAndFamily;

    pTM->tmDigitizedAspectX = pdf->dfHorizRes;
    pTM->tmDigitizedAspectY = pdf->dfVertRes;
}

/***********************************************************************
 *              XFONT_GetFontMetric
 *
 * Retrieve font metric info (enumeration).
 */
static UINT XFONT_GetFontMetric( fontInfo* pfi, LPENUMLOGFONTEX16 pLF,
                                                  LPNEWTEXTMETRIC16 pTM )
{
    memset( pLF, 0, sizeof(*pLF) );
    memset( pTM, 0, sizeof(*pTM) );

#define plf ((LPLOGFONT16)pLF)
    plf->lfHeight    = pTM->tmHeight       = pfi->df.dfPixHeight;
    plf->lfWidth     = pTM->tmAveCharWidth = pfi->df.dfAvgWidth;
    plf->lfWeight    = pTM->tmWeight       = pfi->df.dfWeight;
    plf->lfItalic    = pTM->tmItalic       = pfi->df.dfItalic;
    plf->lfUnderline = pTM->tmUnderlined   = pfi->df.dfUnderline;
    plf->lfStrikeOut = pTM->tmStruckOut    = pfi->df.dfStrikeOut;
    plf->lfCharSet   = pTM->tmCharSet      = pfi->df.dfCharSet;

    /* convert pitch values */

    pTM->tmPitchAndFamily = pfi->df.dfPitchAndFamily;
    plf->lfPitchAndFamily = (pfi->df.dfPitchAndFamily & 0xF1) + 1;

    lstrcpynA( plf->lfFaceName, pfi->df.dfFace, LF_FACESIZE );
#undef plf

    pTM->tmAscent = pfi->df.dfAscent;
    pTM->tmDescent = pTM->tmHeight - pTM->tmAscent;
    pTM->tmInternalLeading = pfi->df.dfInternalLeading;
    pTM->tmMaxCharWidth = pfi->df.dfMaxWidth;
    pTM->tmDigitizedAspectX = pfi->df.dfHorizRes;
    pTM->tmDigitizedAspectY = pfi->df.dfVertRes;

    *(INT*)&pTM->tmFirstChar = *(INT*)&pfi->df.dfFirstChar;

    /* return font type */

    return pfi->df.dfType;
}


/***********************************************************************
 *           XFONT_FixupFlags
 *
 * INIT ONLY
 * 
 * dfPitchAndFamily flags for some common typefaces.
 */
static BYTE XFONT_FixupFlags( LPCSTR lfFaceName )
{
   switch( lfFaceName[0] )
   {
        case 'h':
        case 'H': if(!strcasecmp(lfFaceName, "Helvetica") )
                    return FF_SWISS;
                  break;
        case 'c':
        case 'C': if(!strcasecmp(lfFaceName, "Courier") ||
		     !strcasecmp(lfFaceName, "Charter") )
                    return FF_ROMAN;
                  break;
        case 'p':
        case 'P': if( !strcasecmp(lfFaceName,"Palatino") )
                    return FF_ROMAN;
                  break;
        case 't':
        case 'T': if(!strncasecmp(lfFaceName, "Times", 5) )
                    return FF_ROMAN;
                  break;
        case 'u':
        case 'U': if(!strcasecmp(lfFaceName, "Utopia") )
                    return FF_ROMAN;
                  break;
        case 'z':
        case 'Z': if(!strcasecmp(lfFaceName, "Zapf Dingbats") )
                    return FF_DECORATIVE;
   }
   return 0;
}


/***********************************************************************
 *           XFONT_CheckResourceName
 *
 * INIT ONLY
 */
static BOOL XFONT_CheckResourceName( LPSTR resource, LPCSTR name, INT n )
{
    resource = LFD_Advance( resource, 2 );
    if( resource )
	return (!strncasecmp( resource, name, n ));
    return FALSE;
}


/***********************************************************************
 *           XFONT_WindowsNames
 *
 * INIT ONLY
 *
 * Build generic Windows aliases for X font names.
 *
 * -misc-fixed- -> "Fixed"
 * -sony-fixed- -> "Sony Fixed", etc...
 */
static void XFONT_WindowsNames( char* buffer )
{
    fontResource* fr, *pfr;
    char* 	lpstr, *lpch;
    int		i, up;
    BYTE	bFamilyStyle;
    const char*	relocTable[3];
    
    relocTable[0] = INIDefaultFixed;
    relocTable[1] = INIDefault;
    relocTable[2] = NULL;

    for( fr = fontList; fr ; fr = fr->next )
    {
	if( fr->fr_flags & FR_NAMESET ) continue;     /* skip already assigned */

	lpstr = LFD_Advance(fr->resource, 2);
	i = LFD_Advance( lpstr, 1 ) - lpstr;

	for( pfr = fontList; pfr != fr ; pfr = pfr->next )
	    if( pfr->fr_flags & FR_NAMESET )
		if( XFONT_CheckResourceName( pfr->resource, lpstr, i ) )
		    break;

	if( pfr != fr )  /* prepend vendor name */
	    lpstr = fr->resource + 1;

	for( i = 0, up = 1, lpch = fr->lfFaceName; *lpstr && i < 32;
					        lpch++, lpstr++, i++ )
	{
	    if( *lpstr == LFDSeparator[1] || *lpstr == ' ' ) 
	    { 
		*lpch = ' '; 
		up = 1; continue; 
	    }
            else if( isalpha(*lpstr) && up )
	    { 
		*lpch = toupper(*lpstr); 
		up = 0; continue; 
	    }
	    *lpch = *lpstr;
	}
	while (*(lpch - 1) == ' ') *(--lpch) = '\0';

	if( (bFamilyStyle = XFONT_FixupFlags( fr->lfFaceName )) )
	{
	    fontInfo* fi;
	    for( fi = fr->fi ; fi ; fi = fi->next )
		fi->df.dfPitchAndFamily |= bFamilyStyle;
	}

	TRACE("typeface \'%s\'\n", fr->lfFaceName);

	fr->fr_flags |= FR_NAMESET;
    }

    for( up = 0; relocTable[up]; up++ )
	if( PROFILE_GetWineIniString( INIFontSection, relocTable[up], "", buffer, 128 ) )
	{
	    while( *buffer && isspace(*buffer) ) buffer++;
	    for( fr = NULL, pfr = fontList; pfr; pfr = pfr->next )
	    {
		i = strlen( pfr->resource );
		if( !strncasecmp( pfr->resource, buffer, i) )
		{
		    if( fr )
		    {
			fr->next = pfr->next;
			pfr->next = fontList;
			fontList = pfr;
		    }
		    break;
		}
		fr = pfr;
	    }
	}
}

/***********************************************************************
 *           XFONT_CreateAlias
 */
static fontAlias* XFONT_CreateAlias( LPCSTR lpTypeFace, LPCSTR lpAlias )
{
    int j;
    fontAlias* pfa = aliasTable;

    while( 1 ) 
    {
	/* check if we already got one */
	if( !strcasecmp( pfa->faTypeFace, lpAlias ) )
	{
	    TRACE("\tredundant alias '%s' -> '%s'\n", 
		  lpAlias, lpTypeFace );
	    return NULL;
	} 
	if( pfa->next ) pfa = pfa->next;
	else break;
    }

    j = lstrlenA(lpTypeFace) + 1;
    pfa->next = HeapAlloc( SystemHeap, 0, sizeof(fontAlias) +
					  j + lstrlenA(lpAlias) + 1 );
    if((pfa = pfa->next))
    {
	pfa->next = NULL;
	pfa->faTypeFace = (LPSTR)(pfa + 1);
	lstrcpyA( pfa->faTypeFace, lpTypeFace );
        pfa->faAlias = pfa->faTypeFace + j;
        lstrcpyA( pfa->faAlias, lpAlias );

        TRACE("\tadded alias '%s' for %s\n", lpAlias, lpTypeFace );

	return pfa;
    }
    return NULL;
}

/***********************************************************************
 *           XFONT_LoadAliases
 *
 * Read user-defined aliases from wine.conf. Format is as follows
 *
 * Alias# = [Windows font name],[LFD font name], <substitute original name>
 *
 * Example:
 *   Alias0 = Arial, -adobe-helvetica- 
 *   Alias1 = Times New Roman, -bitstream-courier-, 1
 *   ...
 *
 * Note that from 081797 and on we have built-in alias templates that take
 * care of the necessary Windows typefaces.
 */
static void XFONT_LoadAliases( char** buffer, int *buf_size )
{
    char* lpResource, *lpAlias;
    char  subsection[32];
    int	  i = 0, j = 0;
    BOOL bHaveAlias = TRUE, bSubst = FALSE;

    if( *buf_size < 128 )
    {
	*buffer = HeapReAlloc(SystemHeap, 0, *buffer, 256 );
	*buf_size = 256;
    }
    do
    {
	if( j < faTemplateNum )
	{
	    /* built-in templates first */

	    lpResource = faTemplate[j].fatResource;
	    lpAlias = faTemplate[j].fatAlias;
	    j++;
	}
	else
	{
	    /* then WINE.CONF */

	    wsprintfA( subsection, "%s%i", INIAliasSection, i++ );

	    if( (bHaveAlias = PROFILE_GetWineIniString( INIFontSection, 
					subsection, "", *buffer, 128 )) )
	    {
		lpAlias = *buffer;
		while( isspace(*lpAlias) ) lpAlias++;
		lpResource = PROFILE_GetStringItem( lpAlias );
		bSubst = (PROFILE_GetStringItem( lpResource )) ? TRUE : FALSE;
	    }
	}

	if( bHaveAlias )
	{
	    int length;

	    length = strlen( lpAlias );
	    if( lpResource && length )
	    {
		fontResource* fr, *frMatch = NULL;

		for (fr = fontList; fr ; fr = fr->next)
		{
		    if( !strcasecmp( fr->resource, lpResource ) ) frMatch = fr;
		    if( XFONT_CheckResourceName( fr->resource, lpAlias, length ) )
		    {
			/* alias is not needed since the real font is present */
			frMatch = NULL; break;
		    }
		}

		if( frMatch )
		{
		    if( bSubst )
		    {
			fontAlias *pfa, *prev = NULL;

			for(pfa = aliasTable; pfa; pfa = pfa->next)
			{
	/* Remove lpAlias from aliasTable - we should free the old entry */
			    if(!strcmp(lpAlias, pfa->faAlias))
			    {
				if(prev)
				    prev->next = pfa->next;
				else
				    aliasTable = pfa->next;
			     }

	/* Update any references to the substituted font in aliasTable */
			    if(!strcmp(frMatch->lfFaceName,
							pfa->faTypeFace))
				pfa->faTypeFace = HEAP_strdupA( SystemHeap, 0,
							 lpAlias );
			    prev = pfa;
			}
						
                        TRACE("\tsubstituted '%s' with %s\n",
						frMatch->lfFaceName, lpAlias );

			lstrcpynA( frMatch->lfFaceName, lpAlias, LF_FACESIZE );
			frMatch->fr_flags |= FR_NAMESET;
		    }
		    else
		    {
			/* create new entry in the alias table */
			XFONT_CreateAlias( frMatch->lfFaceName, lpAlias );
		    }
		}
	    }
	    else ERR(" malformed font alias '%s'\n", *buffer );
	}
	else break;
    } while(TRUE);
}

/***********************************************************************
 *           XFONT_LoadPenalties
 *
 * Removes specified fonts from the font table to prevent Wine from
 * using it.
 *
 * Ignore# = [LFD font name]
 *
 * Example:
 *   Ignore0 = -misc-nil-
 *   Ignore1 = -sun-open look glyph-
 *   ...
 *
 */
static void XFONT_LoadPenalties( char** buffer, int *buf_size )
{
    int i = 0;
    char  subsection[32];

    if( *buf_size < 128 )
    {
        *buffer = HeapReAlloc(SystemHeap, 0, *buffer, 256 );
        *buf_size = 256;
	*buffer = '\0';
    }
    do
    {
	wsprintfA( subsection, "%s%i", INIIgnoreSection, i++ );

	if( PROFILE_GetWineIniString( INIFontSection,
				subsection, "", *buffer, 255 ) )
	{
	    fontResource** ppfr;
            int length = strlen( *buffer );

	    for( ppfr = &fontList; *ppfr ; ppfr = &((*ppfr)->next))
	    {
		if( !strncasecmp( (*ppfr)->resource, *buffer, length ) )
		{
		    TRACE("Ignoring '%s'\n", (*ppfr)->resource );

		    XFONT_RemoveFontResource( ppfr );
		}
	    }
	}
	else 
	    break;
    } while(TRUE);
}


/***********************************************************************
 *           XFONT_UserMetricsCache
 * 
 * Returns expanded name for the ~/.wine/.cachedmetrics file.
 * Now it also appends the current value of the $DISPLAY varaible.
 */
static char* XFONT_UserMetricsCache( char* buffer, int* buf_size )
{
    char* pchDisplay, *home;

    pchDisplay = getenv( "DISPLAY" );
    if( !pchDisplay ) pchDisplay = "0";

    if ((home = getenv( "HOME" )) != NULL)
    {
	int i = strlen( home ) + strlen( INIWinePrefix ) + 
		strlen( INIFontMetrics ) + strlen( pchDisplay ) + 2;
	if( i > *buf_size ) 
	    buffer = (char*) HeapReAlloc( SystemHeap, 0, buffer, *buf_size = i );
	strcpy( buffer, home );
	strcat( buffer, INIWinePrefix );
	strcat( buffer, INIFontMetrics );
	strcat( buffer, pchDisplay );
    } else buffer[0] = '\0';
    return buffer;
}


/***********************************************************************
 *           XFONT_ReadCachedMetrics
 *
 * INIT ONLY
 */
static BOOL XFONT_ReadCachedMetrics( int fd, int res, unsigned x_checksum, int x_count )
{
    if( fd >= 0 )
    {
	unsigned u;
	int i, j;

	/* read checksums */
	read( fd, &u, sizeof(unsigned) );
	read( fd, &i, sizeof(int) );

	if( u == x_checksum && i == x_count )
	{
	    off_t length, offset = 3 * sizeof(int);

            /* read total size */
	    read( fd, &i, sizeof(int) );
	    length = lseek( fd, 0, SEEK_END );

	    if( length == (i + offset) )
	    {
		lseek( fd, offset, SEEK_SET );
		fontList = (fontResource*)HeapAlloc( SystemHeap, 0, i);
		if( fontList )
		{
		    fontResource* 	pfr = fontList;
		    fontInfo* 		pfi = NULL;

		    TRACE("Reading cached font metrics:\n");

		    read( fd, fontList, i); /* read all metrics at once */
		    while( offset < length )
		    {
			offset += sizeof(fontResource) + sizeof(fontInfo);
			pfr->fi = pfi = (fontInfo*)(pfr + 1);
			j = 1;
			while( TRUE )
			{
			   if( offset > length ||
			      (int)(pfi->next) != j++ ) goto fail;

			   pfi->df.dfFace = pfr->lfFaceName;
			   pfi->df.dfHorizRes = pfi->df.dfVertRes = res;
			   pfi->df.dfPoints = (INT16)(((INT)(pfi->df.dfPixHeight -
				pfi->df.dfInternalLeading) * 72 + (res >> 1)) / res );
			   pfi->next = pfi + 1;

			   if( j > pfr->fi_count ) break;

			   pfi = pfi->next;
			   offset += sizeof(fontInfo);
			}
			pfi->next = NULL;
			if( pfr->next )
			{
			    pfr->next = (fontResource*)(pfi + 1);
			    pfr = pfr->next;
			} 
			else break;
		    }
		    if( pfr->next == NULL &&
			*(int*)(pfi + 1) == X_FMC_MAGIC )
		    {
			/* read LFD stubs */
			char* lpch = (char*)((int*)(pfi + 1) + 1);
			offset += sizeof(int);
			for( pfr = fontList; pfr; pfr = pfr->next )
			{
			    TRACE("\t%s, %i instances\n", lpch, pfr->fi_count );
			    pfr->resource = lpch;
			    while( TRUE )
			    { 
				if( ++offset > length ) goto fail;
				if( !*lpch++ ) break;
			    }
			}
			close( fd );
			return TRUE;
		    }
		}
	    }
	}
fail:
	if( fontList ) HeapFree( SystemHeap, 0, fontList );
	fontList = NULL;
	close( fd );
    }
    return FALSE;
}

/***********************************************************************
 *           XFONT_WriteCachedMetrics
 *
 * INIT ONLY
 */
static BOOL XFONT_WriteCachedMetrics( int fd, unsigned x_checksum, int x_count, int n_ff )
{
    fontResource* pfr;
    fontInfo* pfi;

    if( fd >= 0 )
    {
        int  i, j, k;

        /* font metrics file:
         *
         * +0000 x_checksum
         * +0004 x_count
         * +0008 total size to load
         * +000C prepackaged font metrics
	 * ...
	 * +...x 	X_FMC_MAGIC
	 * +...x + 4 	LFD stubs
         */

	write( fd, &x_checksum, sizeof(unsigned) );
	write( fd, &x_count, sizeof(int) );

	for( j = i = 0, pfr = fontList; pfr; pfr = pfr->next ) 
	{
	    i += strlen( pfr->resource ) + 1;
	    j += pfr->fi_count;
	}
        i += n_ff * sizeof(fontResource) + j * sizeof(fontInfo) + sizeof(int);
	write( fd, &i, sizeof(int) );

	TRACE("Writing font cache:\n");

	for( pfr = fontList; pfr; pfr = pfr->next )
	{
	    fontInfo fi;

	    TRACE("\t%s, %i instances\n", pfr->resource, pfr->fi_count );

	    i = write( fd, pfr, sizeof(fontResource) );
	    if( i == sizeof(fontResource) ) 
	    {
		for( k = 1, pfi = pfr->fi; pfi; pfi = pfi->next )
		{
		    memcpy( &fi, pfi, sizeof(fi) );

		    fi.df.dfFace = NULL;
		    fi.next = (fontInfo*)k;	/* loader checks this */

		    j = write( fd, &fi, sizeof(fi) );
		    k++;
		}
	        if( j == sizeof(fontInfo) ) continue;
	    }
	    break;
	}
        if( i == sizeof(fontResource) && j == sizeof(fontInfo) )
	{
	    i = j = X_FMC_MAGIC;
	    write( fd, &i, sizeof(int) );
	    for( pfr = fontList; pfr && i == j; pfr = pfr->next )
	    {
	        i = strlen( pfr->resource ) + 1;
		j = write( fd, pfr->resource, i );
	    }
	}
	close( fd );
	return ( i == j );
    }
    return TRUE;
}

/***********************************************************************
 *           XFONT_CheckIniSection
 *
 * INIT ONLY
 *
 *   Examines wine.conf for old/invalid font entries and recommend changes to
 *   the user.
 *
 *   Revision history
 *        05-Jul-1997 Dave Cuthbert (dacut@ece.cmu.edu)
 *             Original implementation.
 */
static void XFONT_CheckIniCallback(char const *, char const *, void *);

static char const  *fontmsgprologue =
"Wine warning:\n"
"   The following entries in the [fonts] section of the wine.conf file are\n"
"   obsolete or invalid:\n";

static char const  *fontmsgepilogue =
"   These entries should be eliminated or updated.\n"
"   See the documentation/fonts file for more information.\n";

static int XFONT_CheckIniSection()
{
    int  found = 0;

    PROFILE_EnumerateWineIniSection("Fonts", &XFONT_CheckIniCallback,
                    (void *)&found);
    if(found)
    MESSAGE(fontmsgepilogue);

    return 1;
}

static void  XFONT_CheckIniCallback(
    char const  *key,
    char const  *value,
    void  *found)
{
    /* Ignore any keys that start with potential comment characters "'", '#',
       or ';'. */
    if(key[0] == '\'' || key[0] == '#' || key[0] == ';' || key[0] == '\0')
    return;

    /* Make sure this is a valid key */
    if((strncasecmp(key, INIAliasSection, 5) == 0) ||
       (strncasecmp(key, INIIgnoreSection, 6) == 0) ||
       (strcasecmp( key, INIDefault) == 0) ||
       (strcasecmp( key, INIDefaultFixed) == 0) ||
       (strcasecmp( key, INIGlobalMetrics) == 0) ||
       (strcasecmp( key, INIResolution) == 0) ||
       (strcasecmp( key, INIDefaultSerif) == 0) ||
       (strcasecmp( key, INIDefaultSansSerif) ==0) )
    {
	/* Valid key; make sure the value doesn't contain a wildcard */
	if(strchr(value, '*')) {
	    if(*(int *)found == 0) {
		MESSAGE(fontmsgprologue);
		++*(int *)found;
	    }

	    MESSAGE("     %s=%s [no wildcards allowed]\n", key, value);
	}
    }
    else {
	/* Not a valid key */
	if(*(int *)found == 0) {
	    MESSAGE(fontmsgprologue);
	    ++*(int *)found;
    }

	MESSAGE("     %s=%s [obsolete]\n", key, value);
    }

    return;
}

/***********************************************************************
 *           XFONT_GetPointResolution()
 *
 * INIT ONLY
 *
 * Here we initialize DefResolution which is used in the
 * XFONT_Match() penalty function. We also load the point
 * resolution value (higher values result in larger fonts).
 */
static int XFONT_GetPointResolution( DeviceCaps* pDevCaps )
{
    int i, j, point_resolution, num = 3; 
    int allowed_xfont_resolutions[3] = { 72, 75, 100 };
    int best = 0, best_diff = 65536;

    DefResolution = point_resolution = PROFILE_GetWineIniInt( INIFontSection, INIResolution, 0 );
    if( !DefResolution ) DefResolution = point_resolution = pDevCaps->logPixelsY;
    else pDevCaps->logPixelsX = pDevCaps->logPixelsY = DefResolution;

    for( i = best = 0; i < num; i++ )
    {
	j = abs( DefResolution - allowed_xfont_resolutions[i] );
	if( j < best_diff )
	{
	    best = i;
		best_diff = j;
	}
    }
    DefResolution = allowed_xfont_resolutions[best];
    return point_resolution;
}

/***********************************************************************
 *           XFONT_BuildDefaultAliases
 *
 * INIT ONLY 
 *
 * Alias "Helv", and "Tms Rmn" to the DefaultSansSerif and DefaultSerif
 * fonts respectively.  Create font alias templates for "MS Sans Serif"
 * and "MS Serif", also pointing to DefaultSansSerif and DefaultSerif.
 */
static int XFONT_BuildDefaultAliases( char** buffer, int* buf_size )
{

  aliasTemplate fatDefaultSerif = { "-bitstream-charter-", "Charter" };
  aliasTemplate fatDefaultSansSerif = { "-adobe-helvetica-", "Helvetica" };

  fontResource* fr;

  /* Make sure our buffer is big enough; update calling function's
	buf_size if we change it. */

  if( *buf_size < 128 )
  {
    *buffer = HeapReAlloc( SystemHeap, 0, *buffer, 256 );
    *buf_size = 256;
  }

  /* Get the X11 name of the default serif font from the Wine INI file.
	(-bitstream-charter- is the default.) */

  PROFILE_GetWineIniString( INIFontSection, INIDefaultSerif,
				fatDefaultSerif.fatResource, *buffer, 128 );

  /* Find the Windows typeface which corresponds to the X11 font. */

  for( fr = fontList; fr; fr = fr->next )
    if( !strcasecmp( fr->resource, *buffer ) ) break;

  /* Update the Alias Table entry for "Tms Rmn" with the default serif font's
	typeface.  Update the Alias Template for "MS Serif" with the default
	serif font's X11 name.  Note that this method leaves us dependant on
	the order of the Alias Table and the Alias Templates.  Also, we don't
	check for or handle a situation in which -bitstream-charter- is not
	available. */

  if( fr )
  {
    TRACE("Using \'%s\' as default serif font\n", fr->lfFaceName);
    aliasTable[1].faTypeFace = fr->lfFaceName;
    faTemplate[1].fatResource = fr->resource;
  }
  else
  {
    WARN("No typeface found for \'%s\'; using \'%s\'\n", *buffer,
						fatDefaultSerif.fatAlias);
    aliasTable[1].faTypeFace = fatDefaultSerif.fatAlias;	/* Charter */
    faTemplate[1].fatResource = fatDefaultSerif.fatResource;
  }

  /* Get the X11 name of the default sans serif font from the Wine INI file.
	(-adobe-helvetica- is the default.) */

  PROFILE_GetWineIniString (INIFontSection, INIDefaultSansSerif,
			fatDefaultSansSerif.fatResource, *buffer, 128 );

  /* Find the Windows typeface which corresponds to the X11 font. */

  for( fr = fontList; fr; fr = fr->next )
    if ( !strcasecmp( fr->resource, *buffer ) ) break;

  /* Update the Alias Table entry for "Helv" with the default sans serif font's
	typeface.  Update the Alias Template for "MS Sans Serif" with the
	default sans serif font's X11 name.  Note that this method leaves us
	dependant on the order of the Alias Table and the Alias Templates.
	Also, we don't check for or handle a situation in which
	-adobe-helvetica- is not available. */

  if( fr )
  {
    TRACE("Using \'%s\' as default sans serif font\n", fr->lfFaceName);
    aliasTable[0].faTypeFace = fr->lfFaceName;
    faTemplate[0].fatResource = fr->resource;
  }
  else
  {
    WARN("No typeface found for \'%s\'; using \'%s\'\n", *buffer,
						fatDefaultSansSerif.fatAlias);
    aliasTable[0].faTypeFace = fatDefaultSansSerif.fatAlias;	/* Helvetica */
    faTemplate[0].fatResource = fatDefaultSansSerif.fatResource;
  }

  return 0;
}

/***********************************************************************
 *           X11DRV_FONT_Init
 *
 * Initialize font resource list and allocate font cache.
 */
BOOL X11DRV_FONT_Init( DeviceCaps* pDevCaps )
{
  XFontStruct*	x_fs;
  fontResource* fr, *pfr;
  fontInfo*	fi, *pfi;
  unsigned	x_checksum;
  int		i, j, res, x_count, fd = -1, buf_size = 0;
  char*		lpstr, *lpch, *lpmetrics, *buffer;
  char**	x_pattern;

  XFONT_CheckIniSection();

  res = XFONT_GetPointResolution( pDevCaps );
      
  x_pattern = TSXListFonts(display, "*", MAX_FONT_FAMILIES * 16, &x_count );

  TRACE("Font Mapper: initializing %i fonts [LPY=%i, XDR=%i, DR=%i]\n", 
				    x_count, pDevCaps->logPixelsY, DefResolution, res);
  for( i = x_checksum = 0; i < x_count; i++ )
  {
#if 0
     printf("%i\t: %s\n", i, x_pattern[i] );
#endif

     j = strlen( x_pattern[i] );
     if( j ) x_checksum ^= __genericCheckSum( x_pattern[i], j );
  }
  x_checksum |= X_PFONT_MAGIC;

  buf_size = 128;
  buffer = HeapAlloc( SystemHeap, 0, buf_size );
  lpmetrics = NULL;

  /* deal with systemwide font metrics cache */

  if( PROFILE_GetWineIniString( INIFontSection, INIGlobalMetrics, "", buffer, 128 ) )
      fd = open( buffer, O_RDONLY );

  if( XFONT_ReadCachedMetrics(fd, res, x_checksum, x_count) == FALSE )
  {
      /* try per-user */
      buffer = XFONT_UserMetricsCache( buffer, &buf_size );
      if( buffer[0] )
      {
	  fd = open( buffer, O_RDONLY );
	  if( XFONT_ReadCachedMetrics(fd, res, x_checksum, x_count) == FALSE )
	      lpmetrics = HEAP_strdupA( SystemHeap, 0, buffer ); /* update later on */
      }
  }

  fi = NULL;
  if( fontList == NULL )	/* build metrics from scratch */
  {
     int n_ff;
     char* typeface;

     for( i = n_ff = 0; i < x_count; i++ )
     {
	typeface = lpch = x_pattern[i];

	lpch = LFD_Advance(typeface, 3); /* extra '-' in the beginning */
	if( !*lpch ) continue;

	lpstr = lpch; 
	j = lpch - typeface;	/* resource name length */

	/* find a family to insert into */

	for( pfr = NULL, fr = fontList; fr; fr = fr->next )
	{
	   if( !strncasecmp(fr->resource, typeface, j) && 
	       strlen(fr->resource) == j ) break;
	   pfr = fr;
	}  

	if( !fi ) fi = (fontInfo*) HeapAlloc(SystemHeap, 0, sizeof(fontInfo));

	if( !fr ) /* add new family */
	{
	   if( n_ff >= MAX_FONT_FAMILIES ) break;
	   if( !LFD_InitFontInfo( fi, lpstr) ) continue;

	   n_ff++;
	   fr = (fontResource*) HeapAlloc(SystemHeap, 0, sizeof(fontResource)); 
	   memset(fr, 0, sizeof(fontResource));
	   fr->resource = (char*) HeapAlloc(SystemHeap, 0, j + 1 );
	   lstrcpynA( fr->resource, typeface, j + 1 );

	   TRACE("    family: %s\n", fr->resource );

	   if( pfr ) pfr->next = fr;
	   else fontList = fr;
	}
	else if( !LFD_InitFontInfo( fi, lpstr) ) continue;

	/* check if we already have something better than "fi" */

	for( pfi = fr->fi, j = 0; pfi && j <= 0; pfi = pfi->next ) 
           if( (j = XFONT_IsSubset( pfi, fi )) < 0 ) 
	       pfi->fi_flags |= FI_SUBSET; /* superseded by "fi" */
	if( j > 0 ) continue;

	/* add new font instance "fi" to the "fr" font resource */

	if( fi->fi_flags & FI_SCALABLE )
	{
	    /* set scalable font height to 24 to get an origin for extrapolation */

	   j = strlen(typeface);  j += 0x10;
	   if( j > buf_size ) 
	       buffer = (char*)HeapReAlloc( SystemHeap, 0, buffer, buf_size = j );

	   lpch = LFD_Advance(typeface, 7);
	   memcpy( buffer, typeface, (j = lpch - typeface) );
	   lpch = LFD_Advance(lpch, 4);
	   sprintf( buffer + j, "%d-%d-%d-*-%c-*-", fi->lfd_height,
                            fi->lfd_decipoints, fi->lfd_resolution,
                                        (*lpch == '-')?'*':*lpch );
	   lpch = LFD_Advance(lpch, 2);
	   strcat( lpstr = buffer, lpch);
	}
	else lpstr = typeface;

	if( (x_fs = TSXLoadQueryFont(display, lpstr)) )
	{
	   fi->df.dfHorizRes = fi->df.dfVertRes = res;

	   XFONT_SetFontMetric( fi, fr, x_fs );
           TSXFreeFont( display, x_fs );

	   TRACE("\t[% 2ipt] '%s'\n", fi->df.dfPoints, typeface );

	   XFONT_CheckFIList( fr, fi, REMOVE_SUBSETS );
	   fi = NULL;	/* preventing reuse */
        }
        else
        {
           ERR("failed to load %s\n", lpstr );

           XFONT_CheckFIList( fr, fi, UNMARK_SUBSETS );
        }
     }

     if( lpmetrics )	 /* update cached metrics */
     {
	 fd = open( lpmetrics, O_CREAT | O_TRUNC | O_RDWR, 0644 ); /* -rw-r--r-- */
	 if( XFONT_WriteCachedMetrics( fd, x_checksum, x_count, n_ff ) == FALSE )
	     if( fd ) remove( lpmetrics );	/* couldn't write entire file */
	 HeapFree( SystemHeap, 0, lpmetrics );
     }
  }

  if( fi ) HeapFree(SystemHeap, 0, fi);
  TSXFreeFontNames(x_pattern);

  /* check if we're dealing with X11 R6 server */

  strcpy(buffer, "-*-*-*-*-normal-*-[12 0 0 12]-*-72-*-*-*-iso8859-1");
  if( (x_fs = TSXLoadQueryFont(display, buffer)) )
  {
       XTextCaps |= TC_SF_X_YINDEP;
       TSXFreeFont(display, x_fs);
  }

  XFONT_WindowsNames( buffer );
  XFONT_BuildDefaultAliases( &buffer, &buf_size );
  XFONT_LoadAliases( &buffer, &buf_size );
  XFONT_LoadPenalties( &buffer, &buf_size );
  HeapFree(SystemHeap, 0, buffer);

  InitializeCriticalSection( &crtsc_fonts_X11 );
  MakeCriticalSectionGlobal( &crtsc_fonts_X11 );

  /* fontList initialization is over, allocate X font cache */

  fontCache = (fontObject*) HeapAlloc(SystemHeap, 0, fontCacheSize * sizeof(fontObject));
  XFONT_GrowFreeList(0, fontCacheSize - 1);

  TRACE("done!\n");

  /* update text caps parameter */

  pDevCaps->textCaps = XTextCaps;

  RAW_ASCENT = TSXInternAtom(display, "RAW_ASCENT", TRUE);
  RAW_DESCENT = TSXInternAtom(display, "RAW_DESCENT", TRUE);
  
  return TRUE;
}


/***********************************************************************
 *           XFONT_RemoveFontResource
 *
 * Caller should check if the font resource is in use. If it is it should
 * set FR_REMOVED flag to delay removal until the resource is not in use
 * anymore.
 */
void XFONT_RemoveFontResource( fontResource** ppfr )
{
    fontInfo* pfi;
    fontResource* pfr = *ppfr;

    *ppfr = pfr->next;
    while( pfr->fi )
    {
	pfi = pfr->fi->next;
	HeapFree( SystemHeap, 0, pfr->fi );
	pfr->fi = pfi;
    }
    HeapFree( SystemHeap, 0, pfr );
}

/***********************************************************************
 *           X Font Matching
 *
 * Compare two fonts (only parameters set by the XFONT_InitFontInfo()).
 */
static INT XFONT_IsSubset(fontInfo* match, fontInfo* fi)
{
  INT           m;

  /* 0 - keep both, 1 - keep match, -1 - keep fi */

  m = (BYTE*)&fi->df.dfPixWidth - (BYTE*)&fi->df.dfItalic;
  if( memcmp(&match->df.dfItalic, &fi->df.dfItalic, m )) return 0;

  if( (!((fi->fi_flags & FI_SCALABLE) + (match->fi_flags & FI_SCALABLE))
       && fi->lfd_height != match->lfd_height) ||
      (!((fi->fi_flags & FI_POLYWEIGHT) + (match->fi_flags & FI_POLYWEIGHT))
       && fi->df.dfWeight != match->df.dfWeight) ) return 0;

  m = (int)(match->fi_flags & (FI_POLYWEIGHT | FI_SCALABLE)) -
      (int)(fi->fi_flags & (FI_SCALABLE | FI_POLYWEIGHT));

  if( m == (FI_POLYWEIGHT - FI_SCALABLE) ||
      m == (FI_SCALABLE - FI_POLYWEIGHT) ) return 0;	/* keep both */
  else if( m >= 0 ) return 1;	/* 'match' is better */

  return -1;			/* 'fi' is better */
}

/***********************************************************************
 *            XFONT_Match
 *
 * Compute the matching score between the logical font and the device font.
 *
 * contributions from highest to lowest:
 *      charset
 *      fixed pitch
 *      height
 *      family flags (only when the facename is not present)
 *      width
 *      weight, italics, underlines, strikeouts
 *
 * NOTE: you can experiment with different penalty weights to see what happens.
 * http://premium.microsoft.com/msdn/library/techart/f365/f36b/f37b/d38b/sa8bf.htm
 */
static UINT XFONT_Match( fontMatch* pfm )
{
   fontInfo*    pfi = pfm->pfi;         /* device font to match */
   LPLOGFONT16  plf = pfm->plf;         /* wanted logical font */
   UINT       penalty = 0;
   BOOL       bR6 = pfm->flags & FO_MATCH_XYINDEP;    /* from TextCaps */
   BOOL       bScale = pfi->fi_flags & FI_SCALABLE;
   INT        d, h;

   TRACE("\t[ %-2ipt h=%-3i w=%-3i %s%s]\n", pfi->df.dfPoints,
		 pfi->df.dfPixHeight, pfi->df.dfAvgWidth,
		(pfi->df.dfWeight > 400) ? "Bold " : "Normal ",
		(pfi->df.dfItalic) ? "Italic" : "" );

   pfm->flags = 0;

   if( plf->lfCharSet == DEFAULT_CHARSET )
   {
       if( (pfi->df.dfCharSet!= ANSI_CHARSET) && (pfi->df.dfCharSet!=DEFAULT_CHARSET) ) 
	    penalty += 0x200;
   }
   else if (plf->lfCharSet != pfi->df.dfCharSet) penalty += 0x200;

   /* FIXME: Hack to demote symbols and nil fonts.  Should take into
             account if a program ever actually asked for this type of
             font */
   if ( (strcmp(pfm->pfr->lfFaceName,"Symbol")==0) || (strcmp(pfm->pfr->lfFaceName,"Nil")==0) ) 
     penalty += 0x200; /* very stiff penality */

   /* TMPF_FIXED_PITCH means exactly the opposite */

   if( plf->lfPitchAndFamily & FIXED_PITCH ) 
   {
      if( pfi->df.dfPitchAndFamily & TMPF_FIXED_PITCH ) penalty += 0x100;
   }
   else if( !(pfi->df.dfPitchAndFamily & TMPF_FIXED_PITCH) ) penalty += 0x2;

   if( plf->lfHeight > 0 )
       d = (h = pfi->df.dfPixHeight) - plf->lfHeight;
   else if( plf->lfHeight < -1 )
       d = (h = pfi->df.dfPoints) + plf->lfHeight;
   else d = h = 0;	

   if( d && h && plf->lfHeight )
   {
       UINT16	height = ( plf->lfHeight > 0 ) ? plf->lfHeight
			 : ((-plf->lfHeight * pfi->df.dfPixHeight) / h);
       if( bScale ) pfm->height = height;
       else if( (plf->lfQuality != PROOF_QUALITY) && bR6 )
       {
	   if( d > 0 ) 	/* do not shrink raster fonts */
	   {
	       pfm->height = pfi->df.dfPixHeight;
	       penalty += (pfi->df.dfPixHeight - height) * 0x4;
	   }
	   else 	/* expand only in integer multiples */
	   {
	       pfm->height = height - height%pfi->df.dfPixHeight;
	       penalty += (height - pfm->height + 1) * height / pfi->df.dfPixHeight;
	   }
       }
       else /* can't be scaled at all */
       {
           if( plf->lfQuality != PROOF_QUALITY) pfm->flags |= FO_SYNTH_HEIGHT;
           pfm->height = pfi->df.dfPixHeight;
           penalty += (d > 0)? d * 0x8 : -d * 0x10;
       }
   }
   else pfm->height = pfi->df.dfPixHeight;

   if((pfm->flags & FO_MATCH_PAF) &&
      (plf->lfPitchAndFamily & FF_FAMILY) != (pfi->df.dfPitchAndFamily & FF_FAMILY) )
       penalty += 0x10;

   if( plf->lfWidth )
   {
       if( bR6 && bScale ) h = 0; 
       else
       {
	   /* FIXME: not complete */

	   pfm->flags |= FO_SYNTH_WIDTH;
	   h = abs(plf->lfWidth - (pfm->height * pfi->df.dfAvgWidth)/pfi->df.dfPixHeight);
       }
       penalty += h * ( d ) ? 0x2 : 0x1 ;
   }
   else if( !(pfi->fi_flags & FI_NORMAL) ) penalty++;

   if( plf->lfWeight != FW_DONTCARE )
   {
       penalty += abs(plf->lfWeight - pfi->df.dfWeight) / 40;
       if( plf->lfWeight > pfi->df.dfWeight ) pfm->flags |= FO_SYNTH_BOLD;
   } else if( pfi->df.dfWeight >= FW_BOLD ) penalty++;	/* choose normal by default */

   if( plf->lfItalic != pfi->df.dfItalic )
   {
       penalty += 0x4;
       pfm->flags |= FO_SYNTH_ITALIC;
   }

   if( plf->lfUnderline ) pfm->flags |= FO_SYNTH_UNDERLINE;
   if( plf->lfStrikeOut ) pfm->flags |= FO_SYNTH_STRIKEOUT;

   if( penalty && pfi->lfd_resolution != DefResolution ) 
       penalty++;

   TRACE("  returning %i\n", penalty );

   return penalty;
}

/***********************************************************************
 *            XFONT_MatchFIList
 *
 * Scan a particular font resource for the best match.
 */
static UINT XFONT_MatchFIList( fontMatch* pfm )
{
  BOOL        skipRaster = (pfm->flags & FO_MATCH_NORASTER);
  UINT        current_score, score = (UINT)(-1);
  UINT16        origflags = pfm->flags; /* Preserve FO_MATCH_XYINDEP */
  fontMatch     fm = *pfm;

  for( fm.pfi = pfm->pfr->fi; fm.pfi && score; fm.pfi = fm.pfi->next,
      fm.flags = origflags )
  {
     if( skipRaster && !(fm.pfi->fi_flags & FI_SCALABLE) )
         continue;

     current_score = XFONT_Match( &fm );
     if( score > current_score )
     {
        memcpy( pfm, &fm, sizeof(fontMatch) );
        score = current_score;
     }
  }
  return score;
}

/***********************************************************************
 *            XFONT_CheckFIList
 *
 * REMOVE_SUBSETS - attach new fi and purge subsets
 * UNMARK_SUBSETS - remove subset flags from all fi entries
 */
static void XFONT_CheckFIList( fontResource* fr, fontInfo* fi, int action)
{
  int		i = 0;
  fontInfo*     pfi, *prev;

  for( prev = NULL, pfi = fr->fi; pfi; )
  {
    if( action == REMOVE_SUBSETS )
    {
        if( pfi->fi_flags & FI_SUBSET )
        {
	    fontInfo* subset = pfi;

	    i++;
	    fr->fi_count--;
            if( prev ) prev->next = pfi = pfi->next;
            else fr->fi = pfi = pfi->next;
	    HeapFree( SystemHeap, 0, subset );
	    continue;
        }
    }
    else pfi->fi_flags &= ~FI_SUBSET;

    prev = pfi; 
    pfi = pfi->next;
  }

  if( action == REMOVE_SUBSETS )	/* also add the superset */
  {
    if( fi->fi_flags & FI_SCALABLE )
    {
        fi->next = fr->fi;
        fr->fi = fi;
    }
    else if( prev ) prev->next = fi; else fr->fi = fi;
    fr->fi_count++;
  }

  if( i ) TRACE("\t    purged %i subsets [%i]\n", i , fr->fi_count);
}

/***********************************************************************
 *            XFONT_FindFIList
 */
static fontResource* XFONT_FindFIList( fontResource* pfr, const char* pTypeFace )
{
  while( pfr )
  {
    if( !strcasecmp( pfr->lfFaceName, pTypeFace ) ) break;
    pfr = pfr->next;
  }
  return pfr;
}

/***********************************************************************
 *          XFONT_MatchDeviceFont
 *
 * Scan font resource tree.
 */
static BOOL XFONT_MatchDeviceFont( fontResource* start, fontMatch* pfm )
{
    fontResource**	ppfr;
    fontMatch           fm = *pfm;

    pfm->pfi = NULL;
    if( fm.plf->lfFaceName[0] ) 
    {
	fontAlias* fa;
	LPSTR str = NULL;

	for( fa = aliasTable; fa; fa = fa->next )
	     if( !strcmp( fa->faAlias, fm.plf->lfFaceName ) ) 
	     {
		str = fa->faTypeFace;
		break;
	     }
        fm.pfr = XFONT_FindFIList( start, str ? str : fm.plf->lfFaceName );
    }

    if( fm.pfr ) /* match family */
    {
	TRACE("%s\n", fm.pfr->lfFaceName );

	if( fm.pfr->fr_flags & FR_REMOVED )
	    fm.pfr = 0;
	else
	{
	    XFONT_MatchFIList( &fm );
	   *pfm = fm;
	}
    }

    if( !pfm->pfi )      /* match all available fonts */
    {
        UINT          current_score, score = (UINT)(-1);

        fm.flags |= FO_MATCH_PAF;
        for( ppfr = &fontList; *ppfr && score; ppfr = &(*ppfr)->next )
        {
	    if( (*ppfr)->fr_flags & FR_REMOVED )
	    {
		if( (*ppfr)->fo_count == 0 )
		    XFONT_RemoveFontResource( ppfr );
		continue;
	    }

            fm.pfr = *ppfr;

	    TRACE("%s\n", fm.pfr->lfFaceName );

            current_score = XFONT_MatchFIList( &fm );
            if( current_score < score )
            {
                score = current_score;
                *pfm = fm;
            }
        }
    }
    return TRUE;
}


/***********************************************************************
 *           X Font Cache
 */
static void XFONT_GrowFreeList(int start, int end)
{
   /* add all entries from 'start' up to and including 'end' */

   memset( fontCache + start, 0, (end - start + 1) * sizeof(fontObject) );

   fontCache[end].lru = fontLF;
   fontCache[end].count = -1;
   fontLF = start;
   while( start < end )
   {
	fontCache[start].count = -1;
	fontCache[start].lru = start + 1;
	start++;
   } 
}

static fontObject* XFONT_LookupCachedFont( LPLOGFONT16 plf, UINT16* checksum )
{
    UINT16	cs = __lfCheckSum( plf );
    int		i = fontMRU, prev = -1;
   
   *checksum = cs;
    while( i >= 0 )
    {
	if( fontCache[i].lfchecksum == cs &&
	  !(fontCache[i].fo_flags & FO_REMOVED) )
	{
	    /* FIXME: something more intelligent here */

	    if( !memcmp( plf, &fontCache[i].lf,
			 sizeof(LOGFONT16) - LF_FACESIZE ) &&
		!strncasecmp( plf->lfFaceName, fontCache[i].lf.lfFaceName, 
							    LF_FACESIZE ) )
	    {
		/* remove temporarily from the lru list */

		if( prev >= 0 )
		    fontCache[prev].lru = fontCache[i].lru;
		else 
		    fontMRU = (INT16)fontCache[i].lru;
		return (fontCache + i);
	    }
	}
	prev = i;
	i = (INT16)fontCache[i].lru;
    }
    return NULL;
}

static fontObject* XFONT_GetCacheEntry()
{
    int		i;

    if( fontLF == -1 )
    {
	int	prev_i, prev_j, j;

	TRACE("font cache is full\n");

	/* lookup the least recently used font */

	for( prev_i = prev_j = j = -1, i = fontMRU; i >= 0; i = (INT16)fontCache[i].lru )
	{
	    if( fontCache[i].count <= 0 &&
	      !(fontCache[i].fo_flags & FO_SYSTEM) )
	    {
		prev_j = prev_i;
		j = i;
	    }
	    prev_i = i;
	}

	if( j >= 0 )	/* unload font */
	{
	    /* detach from the lru list */

	    TRACE("\tfreeing entry %i\n", j );

	    fontCache[j].fr->fo_count--;

	    if( prev_j >= 0 )
		fontCache[prev_j].lru = fontCache[j].lru;
	    else fontMRU = (INT16)fontCache[j].lru;

	    /* FIXME: lpXForm, lpPixmap */
	    if(fontCache[j].lpX11Trans)
	        HeapFree( SystemHeap, 0, fontCache[j].lpX11Trans );

	    TSXFreeFont( display, fontCache[j].fs );

	    memset( fontCache + j, 0, sizeof(fontObject) );
	    return (fontCache + j);
	}
	else		/* expand cache */
	{
	    fontObject*	newCache;

	    prev_i = fontCacheSize + FONTCACHE;

	    TRACE("\tgrowing font cache from %i to %i\n", fontCacheSize, prev_i );

	    if( (newCache = (fontObject*)HeapReAlloc(SystemHeap, 0,  
						     fontCache, prev_i)) )
	    {
		i = fontCacheSize;
		fontCacheSize  = prev_i;
		fontCache = newCache;
		XFONT_GrowFreeList( i, fontCacheSize - 1);
	    } 
	    else return NULL;
	}
    }

    /* detach from the free list */

    i = fontLF;
    fontLF = (INT16)fontCache[i].lru;
    fontCache[i].count = 0;
    return (fontCache + i);
}

static int XFONT_ReleaseCacheEntry(fontObject* pfo)
{
    UINT	u = (UINT)(pfo - fontCache);

    if( u < fontCacheSize ) return (--fontCache[u].count);
    return -1;
}

/**********************************************************************
 *	XFONT_SetX11Trans
 */
static BOOL XFONT_SetX11Trans( fontObject *pfo )
{
  char *fontName;
  Atom nameAtom;
  char *cp, *start;

  TSXGetFontProperty( pfo->fs, XA_FONT, &nameAtom );
  fontName = TSXGetAtomName( display, nameAtom );
  cp = LFD_Advance( fontName, 7 );
  if(*cp != '[') {
    TSXFree(fontName);
    return FALSE;
  }
  start = cp;
  while((cp = strchr(cp, '~')))
    *cp = '-';

#define PX pfo->lpX11Trans

  sscanf(start, "[%f%f%f%f]", &PX->a, &PX->b, &PX->c, &PX->d);
  TSXFree(fontName);

  TSXGetFontProperty( pfo->fs, RAW_ASCENT, &PX->RAW_ASCENT );
  TSXGetFontProperty( pfo->fs, RAW_DESCENT, &PX->RAW_DESCENT );

  PX->pixelsize = hypot(PX->a, PX->b);
  PX->ascent = PX->pixelsize / 1000.0 * PX->RAW_ASCENT;
  PX->descent = PX->pixelsize / 1000.0 * PX->RAW_DESCENT;

  TRACE("[%f %f %f %f] RA = %ld RD = %ld\n", pfo->lpX11Trans->a, 
	pfo->lpX11Trans->b, pfo->lpX11Trans->c, pfo->lpX11Trans->d,
	pfo->lpX11Trans->RAW_ASCENT, pfo->lpX11Trans->RAW_DESCENT);

#undef PX
  return TRUE;
}

/***********************************************************************
 *           X Device Font Objects
 */
static X_PHYSFONT XFONT_RealizeFont( LPLOGFONT16 plf )
{
    UINT16	checksum;
    fontObject* pfo = XFONT_LookupCachedFont( plf, &checksum );

    if( !pfo )
    {
	fontMatch	fm;
	INT		i, index;

	fm.pfr = NULL;
	fm.pfi = NULL;
	fm.height = 0;
	fm.flags = 0;
	fm.plf = plf;

	if( XTextCaps & TC_SF_X_YINDEP ) fm.flags = FO_MATCH_XYINDEP;

	/* allocate new font cache entry */

	if( (pfo = XFONT_GetCacheEntry()) )
	{
	    LPSTR	lpLFD = HeapAlloc( GetProcessHeap(), 0, MAX_LFD_LENGTH );
	    
	    if( lpLFD ) /* initialize entry and load font */
	    {
		UINT	uRelaxLevel = 0;

		TRACE("(%u) '%s' h=%i weight=%i %s\n",
			     plf->lfCharSet, plf->lfFaceName, plf->lfHeight, 
			     plf->lfWeight, (plf->lfItalic) ? "Italic" : "" );

		if(abs(plf->lfHeight) > MAX_FONT_SIZE) {
		    ERR(
  "plf->lfHeight = %d, Creating a 100 pixel font and rescaling metrics \n", 
			plf->lfHeight);
		    pfo->rescale = fabs(plf->lfHeight / 100.0);
		    if(plf->lfHeight > 0) plf->lfHeight = 100;
		    else plf->lfHeight = -100;
		} else
		    pfo->rescale = 1.0;

		XFONT_MatchDeviceFont( fontList, &fm );

		pfo->fr = fm.pfr;
		pfo->fi = fm.pfi;
		pfo->fr->fo_count++;
		pfo->fo_flags = fm.flags & ~FO_MATCH_MASK;

		memcpy( &pfo->lf, plf, sizeof(LOGFONT16) );
		pfo->lfchecksum = checksum;

		do
		{
		    LFD_ComposeLFD( pfo, fm.height, lpLFD, uRelaxLevel++ );
		    if( (pfo->fs = TSXLoadQueryFont( display, lpLFD )) ) break;
		} while( uRelaxLevel );

		
		if(pfo->lf.lfEscapement != 0) {
		    pfo->lpX11Trans = HeapAlloc(SystemHeap, 0,
						sizeof(XFONTTRANS));
		    if(!XFONT_SetX11Trans( pfo )) {
		        HeapFree(SystemHeap, 0, pfo->lpX11Trans);
			pfo->lpX11Trans = NULL;
		    }
		}

		if( XFONT_GetLeading( &pfo->fi->df, pfo->fs, &i, NULL, 
				      pfo->lpX11Trans ) )

		    if(!pfo->lpX11Trans)
		        pfo->foAvgCharWidth =
	      (INT16)pfo->fs->per_char['X' - pfo->fs->min_char_or_byte2].width;
		    else
		        pfo->foAvgCharWidth = 
	 (INT16)pfo->fs->per_char['X' - pfo->fs->min_char_or_byte2].attributes
			  * pfo->lpX11Trans->pixelsize / 1000.0;
		else
		    pfo->foAvgCharWidth = (INT16)XFONT_GetAvgCharWidth(
			             &pfo->fi->df, pfo->fs, pfo->lpX11Trans );
		pfo->foMaxCharWidth = (INT16)XFONT_GetMaxCharWidth(pfo);
		pfo->foInternalLeading = (INT16)i;

		/* FIXME: If we've got a soft font or
		 * there are FO_SYNTH_... flags for the
		 * non PROOF_QUALITY request, the engine
		 * should rasterize characters into mono
		 * pixmaps and store them in the pfo->lpPixmap
		 * array (pfo->fs should be updated as well).
		 * array (pfo->fs should be updated as well).
		 * X11DRV_ExtTextOut() must be heavily modified 
		 * to support pixmap blitting and FO_SYNTH_...
		 * styles.
		 */

		pfo->lpPixmap = NULL;

		HeapFree( GetProcessHeap(), 0, lpLFD );
	    } 
	    else /* attach back to the free list */
	    {
		pfo->count = -1;
		pfo->lru = fontLF;
		fontLF = (pfo - fontCache);
		pfo = NULL;
	    }
	}

	if( !pfo ) /* couldn't get a new entry, get one of the cached fonts */
	{
	    UINT		current_score, score = (UINT)(-1);

	    i = index = fontMRU; 
	    fm.flags |= FO_MATCH_PAF;
            do
            {
		pfo = fontCache + i;
		fm.pfr = pfo->fr; fm.pfi = pfo->fi;

                current_score = XFONT_Match( &fm );
                if( current_score < score ) index = i;

	        i =  pfo->lru;
            } while( i >= 0 );
	    pfo = fontCache + index;
	    pfo->count++;
	    return (X_PHYSFONT)(X_PFONT_MAGIC | index);
	}
    }
 
    /* attach at the head of the lru list */

    pfo->count++;
    pfo->lru = fontMRU;
    fontMRU = (pfo - fontCache);

    TRACE("physfont %i\n", fontMRU);

    return (X_PHYSFONT)(X_PFONT_MAGIC | fontMRU);
}

/***********************************************************************
 *           XFONT_GetFontObject
 */
fontObject* XFONT_GetFontObject( X_PHYSFONT pFont )
{
    if( CHECK_PFONT(pFont) ) return __PFONT(pFont);
    return NULL;
}

/***********************************************************************
 *           XFONT_GetFontStruct
 */
XFontStruct* XFONT_GetFontStruct( X_PHYSFONT pFont )
{
    if( CHECK_PFONT(pFont) ) return __PFONT(pFont)->fs;
    return NULL;
}

/***********************************************************************
 *           XFONT_GetFontInfo
 */
LPIFONTINFO16 XFONT_GetFontInfo( X_PHYSFONT pFont )
{
    if( CHECK_PFONT(pFont) ) return &(__PFONT(pFont)->fi->df);
    return NULL;
}



/* X11DRV Interface ****************************************************
 *                                                                     *
 * Exposed via the dc->funcs dispatch table.                           *
 *                                                                     *
 ***********************************************************************/
/***********************************************************************
 *           X11DRV_FONT_SelectObject
 */
HFONT X11DRV_FONT_SelectObject( DC* dc, HFONT hfont, FONTOBJ* font )
{
    HFONT hPrevFont = 0;
    LOGFONT16 lf;
    X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;

    EnterCriticalSection( &crtsc_fonts_X11 );

    if( CHECK_PFONT(physDev->font) ) 
        XFONT_ReleaseCacheEntry( __PFONT(physDev->font) );

    /* FIXME: do we need to pass anything back from here? */
    memcpy(&lf,&font->logfont,sizeof(lf));
    lf.lfWidth  = font->logfont.lfWidth * dc->vportExtX/dc->wndExtX;
    lf.lfHeight = font->logfont.lfHeight* dc->vportExtY/dc->wndExtY;

    physDev->font = XFONT_RealizeFont( &lf );
    hPrevFont = dc->w.hFont;
    dc->w.hFont = hfont;

    LeaveCriticalSection( &crtsc_fonts_X11 );

    return hPrevFont;
}


/***********************************************************************
 *
 *           X11DRV_EnumDeviceFonts
 */
BOOL	X11DRV_EnumDeviceFonts( DC* dc, LPLOGFONT16 plf, 
				        DEVICEFONTENUMPROC proc, LPARAM lp )
{
    ENUMLOGFONTEX16	lf;
    NEWTEXTMETRIC16	tm;
    fontResource* 	pfr = fontList;
    BOOL	  	b, bRet = 0;

    if( plf->lfFaceName[0] )
    {
	/* enum all entries in this resource */
	pfr = XFONT_FindFIList( pfr, plf->lfFaceName );
	if( pfr )
	{
	    fontInfo*	pfi;
	    for( pfi = pfr->fi; pfi; pfi = pfi->next )
	    {
		/* Note: XFONT_GetFontMetric() will have to
		   release the crit section, font list will
		   have to be retraversed on return */

		if( (b = (*proc)( (LPENUMLOGFONT16)&lf, &tm, 
			XFONT_GetFontMetric( pfi, &lf, &tm ), lp )) )
		     bRet = b;
		else break;
	    }
	}
    }
    else /* enum first entry in each resource */
	for( ; pfr ; pfr = pfr->next )
	{
            if(pfr->fi)
            {
		if( (b = (*proc)( (LPENUMLOGFONT16)&lf, &tm, 
			XFONT_GetFontMetric( pfr->fi, &lf, &tm ), lp )) )
		     bRet = b;
		else break;
            }
	}
    return bRet;
}


/***********************************************************************
 *           X11DRV_GetTextExtentPoint
 */
BOOL X11DRV_GetTextExtentPoint( DC *dc, LPCSTR str, INT count,
                                  LPSIZE size )
{
    X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
    fontObject* pfo = XFONT_GetFontObject( physDev->font );
    if( pfo ) {
        if( !pfo->lpX11Trans ) {
	    int dir, ascent, descent;
	    XCharStruct info;

	    TSXTextExtents( pfo->fs, str, count, &dir, &ascent, &descent, &info );
	    size->cx = abs((info.width + dc->w.breakRem + count * 
			    dc->w.charExtra) * dc->wndExtX / dc->vportExtX);
	    size->cy = abs((pfo->fs->ascent + pfo->fs->descent) * 
			   dc->wndExtY / dc->vportExtY);
	} else {

	    INT i;
	    float x = 0.0, y = 0.0;
	    for(i = 0; i < count; i++) {
	        x += pfo->fs->per_char ? 
	   pfo->fs->per_char[str[i] - pfo->fs->min_char_or_byte2].attributes : 
	   pfo->fs->min_bounds.attributes;
	    }
	    y = pfo->lpX11Trans->RAW_ASCENT + pfo->lpX11Trans->RAW_DESCENT;
	    TRACE("x = %f y = %f\n", x, y);
	    x *= pfo->lpX11Trans->pixelsize / 1000.0;
	    y *= pfo->lpX11Trans->pixelsize / 1000.0; 
	    size->cx = fabs((x + dc->w.breakRem + count * dc->w.charExtra) *
			     dc->wndExtX / dc->vportExtX);
	    size->cy = fabs(y * dc->wndExtY / dc->vportExtY);
	}
	size->cx *= pfo->rescale;
	size->cy *= pfo->rescale;
	return TRUE;
    }
    return FALSE;
}


/***********************************************************************
 *           X11DRV_GetTextMetrics
 */
BOOL X11DRV_GetTextMetrics(DC *dc, TEXTMETRICA *metrics)
{
    X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;

    if( CHECK_PFONT(physDev->font) )
    {
	fontObject* pfo = __PFONT(physDev->font);
	XFONT_GetTextMetric( pfo, metrics );

	return TRUE;
    }
    return FALSE;
}


/***********************************************************************
 *           X11DRV_GetCharWidth
 */
BOOL X11DRV_GetCharWidth( DC *dc, UINT firstChar, UINT lastChar,
                            LPINT buffer )
{
    X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
    fontObject* pfo = XFONT_GetFontObject( physDev->font );

    if( pfo )
    {
	int i;

	if (pfo->fs->per_char == NULL)
	    for (i = firstChar; i <= lastChar; i++)
  	        if(pfo->lpX11Trans)
		    *buffer++ = pfo->fs->min_bounds.attributes *
		      pfo->lpX11Trans->pixelsize / 1000.0 * pfo->rescale;
		else
		    *buffer++ = pfo->fs->min_bounds.width * pfo->rescale;
	else
	{
	    XCharStruct *cs, *def;
	    static XCharStruct	__null_char = { 0, 0, 0, 0, 0, 0 };

	    CI_GET_CHAR_INFO(pfo->fs, pfo->fs->default_char, &__null_char,
			     def);

	    for (i = firstChar; i <= lastChar; i++)
	    {
		if (i >= pfo->fs->min_char_or_byte2 && 
		    i <= pfo->fs->max_char_or_byte2)
		{
		    cs = &pfo->fs->per_char[(i - pfo->fs->min_char_or_byte2)]; 
		    if (CI_NONEXISTCHAR(cs)) cs = def; 
  		} else cs = def;
		if(pfo->lpX11Trans)
		    *buffer++ = MAX(cs->attributes, 0) *
		      pfo->lpX11Trans->pixelsize / 1000.0 * pfo->rescale;
		else
		    *buffer++ = MAX(cs->width, 0 ) * pfo->rescale;
	    }
	}

	return TRUE;
    }
    return FALSE;
}

#endif /* !defined(X_DISPLAY_MISSING) */

/***********************************************************************
 *								       *
 *           Font Resource API					       *
 *								       *
 ***********************************************************************/
/***********************************************************************
 *           AddFontResource16    (GDI.119)
 *
 *  Can be either .FON, or .FNT, or .TTF, or .FOT font file.
 *
 *  FIXME: Load header and find the best-matching font in the fontList;
 * 	   fixup dfPoints if all metrics are identical, otherwise create
 *	   new fontAlias. When soft font support is ready this will
 *	   simply create a new fontResource ('filename' will go into
 *	   the pfr->resource field) with FR_SOFTFONT/FR_SOFTRESOURCE 
 *	   flag set. 
 */
INT16 WINAPI AddFontResource16( LPCSTR filename )
{
    return AddFontResourceA( filename );
}


/***********************************************************************
 *           AddFontResource32A    (GDI32.2)
 */
INT WINAPI AddFontResourceA( LPCSTR str )
{
    FIXME("(%s): stub\n", debugres_a(str));
    return 1;
}


/***********************************************************************
 *           AddFontResource32W    (GDI32.4)
 */
INT WINAPI AddFontResourceW( LPCWSTR str )
{
    FIXME("(%s): stub\n", debugres_w(str) );
    return 1;
}

/***********************************************************************
 *           RemoveFontResource16    (GDI.136)
 */
BOOL16 WINAPI RemoveFontResource16( SEGPTR str )
{
    FIXME("(%s): stub\n",	debugres_a(PTR_SEG_TO_LIN(str)));
    return TRUE;
}


/***********************************************************************
 *           RemoveFontResource32A    (GDI32.284)
 */
BOOL WINAPI RemoveFontResourceA( LPCSTR str )
{
/*  This is how it should look like */
/*
    fontResource** ppfr;
    BOOL32 retVal = FALSE;

    EnterCriticalSection( &crtsc_fonts_X11 );
    for( ppfr = &fontList; *ppfr; ppfr = &(*ppfr)->next )
	 if( !strcasecmp( (*ppfr)->lfFaceName, str ) )
	 {
	     if(((*ppfr)->fr_flags & (FR_SOFTFONT | FR_SOFTRESOURCE)) &&
		 (*ppfr)->hOwnerProcess == GetCurrentProcess() )
	     {
		 if( (*ppfr)->fo_count )
		     (*ppfr)->fr_flags |= FR_REMOVED;
		 else
		     XFONT_RemoveFontResource( ppfr );
	     }
	     retVal = TRUE;
	 }
    LeaveCriticalSection( &crtsc_fontList );
    return retVal;
 */
    FIXME("(%s): stub\n", debugres_a(str));
    return TRUE;
}


/***********************************************************************
 *           RemoveFontResource32W    (GDI32.286)
 */
BOOL WINAPI RemoveFontResourceW( LPCWSTR str )
{
    FIXME("(%s): stub\n", debugres_w(str) );
    return TRUE;
}

