/**************************************************************************/
/*                                                                        */
/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com)          */
/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de>  */
/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de>                */
/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de>                 */
/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com)           */
/*                                                                        */
/* NXAGENT, NX protocol compression and NX extensions to this software    */
/* are copyright of the aforementioned persons and companies.             */
/*                                                                        */
/* Redistribution and use of the present software is allowed according    */
/* to terms specified in the file LICENSE which comes in the source       */
/* distribution.                                                          */
/*                                                                        */
/* All rights reserved.                                                   */
/*                                                                        */
/* NOTE: This software has received contributions from various other      */
/* contributors, only the core maintainers and supporters are listed as   */
/* copyright holders. Please contact us, if you feel you should be listed */
/* as copyright holder, as well.                                          */
/*                                                                        */
/**************************************************************************/

/*

Copyright 1993 by Davor Matic

Permission to use, copy, modify, distribute, and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation.  Davor Matic makes no representations about
the suitability of this software for any purpose.  It is provided "as
is" without express or implied warranty.

*/

#include "scrnintstr.h"
#include "dixstruct.h"
#include <X11/fonts/font.h>
#include <X11/fonts/fontstruct.h>
#include "dixfontstr.h"
#include "misc.h"
#include "miscstruct.h"
#include "opaque.h"

#include "Agent.h"

#include "Display.h"
#include "Font.h"
#include "Error.h"

#include <stdio.h>
#include <sys/stat.h>
#include "resource.h"
#include "Reconnect.h"

#include "Args.h"
#include "Utils.h"

#include "compext/Compext.h"
#include <nx/NXalert.h>

#include <string.h>
#include <stdlib.h>

#define PANIC
#define WARNING
#undef  TEST
#undef  DEBUG

const char * nxagentFontDirs[] = {
  SYSTEMFONTDIR,
  "/usr/share/nx/fonts",
  "/usr/share/X11/fonts",
  "/usr/share/fonts/X11",
  "/usr/X11R6/lib/X11/fonts",
  NULL
};

const char * nxagentFontSubdirs[] = {
  "Type1",
  "75dpi",
  "100dpi",
  "TTF",
  NULL
};

#undef NXAGENT_FONTCACHE_DEBUG
#undef NXAGENT_RECONNECT_FONT_DEBUG
#undef NXAGENT_FONTMATCH_DEBUG

#define FIELDS 14

static int reconnectFlexibility;

static void nxagentCleanCacheAfterReconnect(void);
static void nxagentFontReconnect(FontPtr, XID, void *);
static XFontStruct *nxagentLoadBestQueryFont(Display* dpy, char *fontName, FontPtr pFont);
static XFontStruct *nxagentLoadQueryFont(register Display *dpy , char *fontName , FontPtr pFont);
int nxagentFreeFont(XFontStruct *fs);
static Bool nxagentGetFontServerPath(char * fontServerPath, int size);
static char * nxagentMakeScalableFontName(const char *fontName, Bool scalableResolution);

RESTYPE RT_NX_FONT;

#ifdef NXAGENT_RECONNECT_FONT_DEBUG
static void printFontCacheDump(char*);
#endif

typedef struct _nxagentFontRec
{
    char *name;
    int  status;
} nxagentFontRec, *nxagentFontRecPtr;

typedef struct _nxagentFontList
{
    nxagentFontRecPtr *list;
    int length;
    int listSize;
} nxagentFontList, *nxagentFontListPtr;

nxagentFontList nxagentRemoteFontList = {NULL, (int)0, (int)0};

int nxagentFontPrivateIndex;

typedef struct _nxCacheFontEntry
{
    Atom atom;
    XFontStruct *font_struct;
    char *name;
} nxCacheFontEntryRec, *nxCacheFontEntryRecPtr;

static struct _nxagentFontCache
{
    nxCacheFontEntryRecPtr *entry;
    int index;
    int size;
} nxagentFontCache = { NULL, (int) 0, (int) 0 };

#define CACHE_ENTRY_PTR (nxagentFontCache.entry)
#define CACHE_INDEX (nxagentFontCache.index)
#define CACHE_SIZE (nxagentFontCache.size)
#define CACHE_ENTRY(A) (CACHE_ENTRY_PTR[A])
#define CACHE_FSTRUCT(A) (CACHE_ENTRY(A) -> font_struct)
#define CACHE_NAME(A) (CACHE_ENTRY(A) -> name)
#define CACHE_ATOM(A) (CACHE_ENTRY(A) -> atom)

static struct _nxagentFailedToReconnectFonts
{
    FontPtr *font;
    XID *id;
    int size;
    int index;
} nxagentFailedToReconnectFonts = {NULL, NULL, 0, 0};

static void nxagentFreeRemoteFontList(nxagentFontList *listRec);

void nxagentFreeFontData(void)
{
    nxagentFreeFontCache();
    nxagentFreeRemoteFontList(&nxagentRemoteFontList);
}

/*
 * This is used if nxagentFullGeneration is true in CloseDisplay().
 */

void nxagentFreeFontCache(void)
{
    #ifdef NXAGENT_FONTCACHE_DEBUG
    fprintf(stderr, "Font: Freeing nxagent font cache\n");
    #endif

    if (CACHE_INDEX == 0)
        return;

    #ifdef NXAGENT_FONTCACHE_DEBUG
    fprintf(stderr, "Font: Freeing nxagent font cache, there are [%d] entries.\n", CACHE_INDEX);
    #endif

    for (int i = 0; i < CACHE_INDEX; i++)
    {
        #ifdef NXAGENT_FONTCACHE_DEBUG
        fprintf(stderr, "Font: Freeing nxagent font cache entry [%d] entry pointer is [%p], name [%s]\n",
                    i, CACHE_ENTRY(i), CACHE_NAME(i));
        #endif

        if (CACHE_FSTRUCT(i))
        {
            nxagentFreeFont(CACHE_FSTRUCT(i));
        }

        SAFE_free(CACHE_NAME(i));
        SAFE_free(CACHE_ENTRY(i));
    }

    SAFE_free(CACHE_ENTRY_PTR);
    CACHE_INDEX = 0;
    CACHE_SIZE = 0;

    #ifdef NXAGENT_FONTCACHE_DEBUG
    fprintf(stderr, "Font: nxagent font cache fully freed\n");
    #endif

    return;
}

void nxagentListRemoteFonts(const char *searchPattern, const int maxNames)
{
    if (NXDisplayError(nxagentDisplay) == 1)
    {
        return;
    }

    /*
     * Avoid querying again the remote fonts.
     */

    if (nxagentRemoteFontList.length > 0)
    {
        return;
    }

    /*
     * We can't retrieve the full remote font list with a single query,
     * because the number of dashes in the pattern acts as a rule to
     * select how to search for the font names, so the pattern '*' is
     * useful to retrieve the font aliases, while the other one will
     * select the 'real' fonts.
     */

    const char *patterns[] = {"*", "-*-*-*-*-*-*-*-*-*-*-*-*-*-*"};
    const int numPatterns = 2;

    for (int p = 0; p < numPatterns; p++)
    {
        int  xLen = 0;
        char **xList = XListFonts(nxagentDisplay, patterns[p], maxNames, &xLen);

        #ifdef NXAGENT_FONTMATCH_DEBUG
        fprintf(stderr, "nxagentListRemoteFonts: NXagent remote list [%s] has %d elements.\n", patterns[p], xLen);
        #endif

        /*
         * Add the ListFont request pattern to the list with the last
         * requested maxnames.
         */

        nxagentListRemoteAddName(searchPattern, maxNames);

        for (int i = 0; i < xLen; i++)
        {
            nxagentListRemoteAddName(xList[i], 1);
        }

        XFreeFontNames(xList);
    }

    #ifdef NXAGENT_FONTMATCH_DEBUG

    fprintf(stderr, "nxagentListRemoteFonts: Printing remote font list.\n");

    for (int i = 0; i < nxagentRemoteFontList.length; i++)
    {
        fprintf(stderr, "Font# %d, \"%s\"\n", i, nxagentRemoteFontList.list[i]->name);
    }

    fprintf(stderr, "nxagentListRemoteFonts: End of list\n");

    #endif
}

void nxagentListRemoteAddName(const char *name, int status)
{
    int pos;

    if (nxagentFontFind(name, &pos))
    {
        if (nxagentRemoteFontList.list[pos]->status < status)
        {
            nxagentRemoteFontList.list[pos]->status = status;

            #ifdef NXAGENT_FONTMATCH_DEBUG
            fprintf(stderr, "Font: Font# %d, [%s] change status to %s\n",
                        pos, nxagentRemoteFontList.list[pos]->name,
                            nxagentRemoteFontList.list[pos]->status ? "OK" : "deleted");
            #endif
        }
        return;
    }

    if (nxagentRemoteFontList.length == nxagentRemoteFontList.listSize)
    {
        int num = nxagentRemoteFontList.listSize + 1000;
        nxagentFontRecPtr *tmp1 = realloc(nxagentRemoteFontList.list, sizeof(nxagentFontRecPtr) * num);

        if (tmp1 == NULL)
        {
            FatalError("Font: remote list memory re-allocation failed!.\n");
        }

        nxagentRemoteFontList.list = tmp1;
        nxagentRemoteFontList.listSize = num;
    }

    if (pos < nxagentRemoteFontList.length)
    {
        #ifdef NXAGENT_FONTMATCH_DEBUG
        fprintf(stderr, "Font: Going to move list from %p to %p len = %d!.\n",
                    &nxagentRemoteFontList.list[pos], &nxagentRemoteFontList.list[pos+1],
                        (nxagentRemoteFontList.length - pos) * sizeof(nxagentFontRecPtr));
        #endif

        memmove(&nxagentRemoteFontList.list[pos+1],
                    &nxagentRemoteFontList.list[pos],
                        (nxagentRemoteFontList.length - pos) * sizeof(nxagentFontRecPtr));
    }

    if ((nxagentRemoteFontList.list[pos] = malloc(sizeof(nxagentFontRec))))
    {
        nxagentRemoteFontList.list[pos]->name = strdup(name);
        if (nxagentRemoteFontList.list[pos]->name == NULL)
        {
            fprintf(stderr, "Font: remote list name memory allocation failed!.\n");
            SAFE_free(nxagentRemoteFontList.list[pos]);
            return;
        }
    }
    else
    {
        fprintf(stderr, "Font: remote list record memory allocation failed!.\n");
        return;
    }
    nxagentRemoteFontList.list[pos]->status = status;
    nxagentRemoteFontList.length++;

    #ifdef NXAGENT_FONTMATCH_DEBUG
    fprintf(stderr, "Font: remote font list added [%s] in position [%d] as %s !.\n",
                name, pos, status ? "OK" : "deleted");
    fprintf(stderr, "Font: remote font list total len is [%d] Size is [%d] !.\n",
                nxagentRemoteFontList.length, nxagentRemoteFontList.listSize);
    #endif
}

static void nxagentFreeRemoteFontList(nxagentFontList *listRec)
{
    for (int l = 0; l < listRec -> length; l++)
    {
        if (listRec -> list[l])
        {
            SAFE_free(listRec -> list[l] -> name);
            SAFE_free(listRec -> list[l]);
        }
    }

    listRec -> length = listRec -> listSize = 0;

    SAFE_free(listRec -> list);

    return;
}

Bool nxagentFontFind(const char *name, int *pos)
{
    if (!nxagentRemoteFontList.length)
    {
        *pos=0;
        return False;
    }

    int low = 0;
    int high = nxagentRemoteFontList.length - 1;
    int iter = 0;
    int res = 1;
    int lpos = nxagentRemoteFontList.length;
    while (low <= high)
    {
        *pos = (high + low)/2;
        iter ++;
        res = strcasecmp(nxagentRemoteFontList.list[*pos]->name,name);
        if (res > 0)
        {
            high = *pos - 1;
            lpos = *pos;
            continue;
        }
        else if (res < 0)
        {
            low = *pos + 1;
            lpos = low;
            continue;
        }
        break;
    }
    *pos = (res == 0) ? *pos : lpos;

    #ifdef NXAGENT_FONTMATCH_DEBUG
    if (res == 0)
        fprintf(stderr, "Font: font found in %d iterations in pos = %d\n", iter, *pos);
    else
        fprintf(stderr, "Font: not font found in %d iterations insertion pos is = %d\n", iter, *pos);
    #endif

    return (res == 0);
}

Bool nxagentFontLookUp(const char *name)
{
    int i;

    if (name && strlen(name) == 0)
    {
        return False;
    }

    Bool result = nxagentFontFind(name, &i);

    char *scalable = NULL;

    /*
     * Let's try with the scalable font description.
     */

    if (!result)
    {
        if ((scalable = nxagentMakeScalableFontName(name, False)) != NULL)
        {
            result = nxagentFontFind(scalable, &i);

            SAFE_free(scalable);
        }
    }

    /*
     * Let's try again after replacing zero to xdpi and ydpi in the pattern.
     */

    if (!result)
    {
        if ((scalable = nxagentMakeScalableFontName(name, True)) != NULL)
        {
            result = nxagentFontFind(scalable, &i);

            SAFE_free(scalable);
        }
    }

    if (!result)
    {
        return False;
    }
    else
    {
        return (nxagentRemoteFontList.list[i]->status > 0);
    }
}

/*
 * NXAGENT uses useless screen pointer to pass the original font name
 * to realizeFont, could be a source of problems in the future.
 */
Bool nxagentRealizeFont(ScreenPtr pScreen, FontPtr pFont)
{
#ifdef HAS_XFONT2
    xfont2_font_set_private(pFont, nxagentFontPrivateIndex, NULL);
#else
    FontSetPrivate(pFont, nxagentFontPrivateIndex, NULL);
#endif /* HAS_XFONT2 */

    Atom name_atom = MakeAtom("FONT", 4, True);
    Atom value_atom = 0L;

    int nprops = pFont->info.nprops;
    FontPropPtr props = pFont->info.props;

    for (int i = 0; i < nprops; i++)
    {
        if ((Atom)props[i].name == name_atom)
        {
            value_atom = props[i].value;
            break;
        }
    }

    if (!value_atom)
        return False;

    const char *name = NameForAtom(value_atom);

    #ifdef NXAGENT_FONTCACHE_DEBUG
    fprintf(stderr, "Font: nxagentRealizeFont, realizing font: %s\n", validateString(name));
    fprintf(stderr, "                                 atom: %ld\n", value_atom);
    fprintf(stderr, "Font: Cache dump:\n");
    for (int i = 0; i < CACHE_INDEX; i++)
    {
        fprintf(stderr, "nxagentFontCache.entry[%d]->name: %s font_struct at %p\n",
                    i, CACHE_NAME(i), CACHE_FSTRUCT(i));
    }
    #endif

    if (!name)
        return False;

    char *origName = (char*) pScreen;
    if ((strcasecmp(origName, name) != 0) && !strchr(origName,'*'))
    {
        #ifdef NXAGENT_FONTMATCH_DEBUG
        fprintf(stderr, "Font: Changing font name to realize from [%s] to [%s]\n",
                    validateString(name), origName);
        #endif

        name = origName;
    }

    void *priv = (void *)malloc(sizeof(nxagentPrivFont));
#ifdef HAS_XFONT2
    xfont2_font_set_private(pFont, nxagentFontPrivateIndex, priv);
#else
    FontSetPrivate(pFont, nxagentFontPrivateIndex, priv);
#endif /* HAS_XFONT2 */

    nxagentFontPriv(pFont) -> mirrorID = 0;

    int fci;
    for (fci = 0; fci < nxagentFontCache.index; fci++)
    {
        /*      if (value_atom == CACHE_ATOM(i))*/
        if (strcasecmp(CACHE_NAME(fci), name) == 0)
        {
            #ifdef NXAGENT_FONTCACHE_DEBUG
            fprintf(stderr, "Font: nxagentFontCache hit [%s] = [%s]!\n", CACHE_NAME(fci), validateString(name));
            #endif

            break;
        }
    }

    if (fci < CACHE_INDEX)
    {
        nxagentFontPriv(pFont)->font_struct = CACHE_FSTRUCT(fci);
        strcpy(nxagentFontPriv(pFont)->fontName, name);
    }
    else
    {
        #ifdef NXAGENT_FONTCACHE_DEBUG
        fprintf(stderr, "Font: nxagentFontCache fail.\n");
        #endif

        if (CACHE_INDEX == CACHE_SIZE)
        {
            int num = CACHE_SIZE + 100;

            nxCacheFontEntryRecPtr *tmp1 = realloc(CACHE_ENTRY_PTR,
                                                       sizeof(nxCacheFontEntryRecPtr) * num);

            if (tmp1 == NULL)
            {
                FatalError("Font: Cache list memory re-allocation failed.\n");
            }

            CACHE_ENTRY_PTR = tmp1;
            CACHE_SIZE = num;
        }

        CACHE_ENTRY(CACHE_INDEX) = malloc(sizeof(nxCacheFontEntryRec));

        if (CACHE_ENTRY(CACHE_INDEX) == NULL)
        {
            return False;
        }

        CACHE_NAME(CACHE_INDEX) = malloc(strlen(name) + 1);

        if (CACHE_NAME(CACHE_INDEX) == NULL)
        {
            return False;
        }

        #ifdef NXAGENT_FONTMATCH_DEBUG
        fprintf(stderr, "Font: Going to realize font [%s],[%s] on real X server.\n", validateString(name), origName);
        #endif

        if (nxagentRemoteFontList.length == 0 && (NXDisplayError(nxagentDisplay) == 0))
        {
            nxagentListRemoteFonts("*", nxagentMaxFontNames);
        }

        nxagentFontPriv(pFont)->font_struct = nxagentLoadQueryFont(nxagentDisplay, (char *)name, pFont);
        strcpy(nxagentFontPriv(pFont)->fontName, name);
        if (nxagentFontPriv(pFont)->font_struct != NULL)
        {
            CACHE_ATOM(fci) = value_atom;
            strcpy(CACHE_NAME(fci), name);
            CACHE_FSTRUCT(fci) = nxagentFontPriv(pFont)->font_struct;
            CACHE_INDEX++;

            nxagentFontPriv(pFont) -> mirrorID = FakeClientID(serverClient -> index);
            AddResource(nxagentFontPriv(pFont) -> mirrorID, RT_NX_FONT, pFont);

            #ifdef NXAGENT_FONTCACHE_DEBUG
            fprintf(stderr, "Font: nxagentFontCache adds font [%s] in pos. [%d].\n",
                        validateString(name), CACHE_INDEX - 1);
            #endif
        }
    }

    #ifdef NXAGENT_FONTMATCH_DEBUG

    if (nxagentFontPriv(pFont)->font_struct == NULL)
    {
        if (!nxagentFontLookUp(name))
        {
            fprintf(stderr, "Font: nxagentRealizeFont failed with font Font=%s, not in our remote list\n",
                        validateString(name));
        }
        else
        {
            fprintf(stderr, "Font: nxagentRealizeFont failed with font Font=%s but the font is in our remote list\n",
                        validateString(name));
        }
    }
    else
    {
        fprintf(stderr, "Font: nxagentRealizeFont OK realizing font Font=%s\n",
                    validateString(name));
    }

    #endif

    return (nxagentFontPriv(pFont)->font_struct != NULL);
}

Bool nxagentUnrealizeFont(ScreenPtr pScreen, FontPtr pFont)
{
    if (nxagentFontPriv(pFont))
    {
        if (NXDisplayError(nxagentDisplay) == 0)
        {
            if (nxagentFontStruct(pFont))
            {
                int fci;

                for (fci = 0; fci < CACHE_INDEX; fci++)
                {
                    if (CACHE_FSTRUCT(fci) == nxagentFontStruct(pFont))
                    {
                        #ifdef NXAGENT_FONTCACHE_DEBUG
                        fprintf(stderr, "nxagentUnrealizeFont: Not freeing the font in cache.\n");
                        #endif

                        break;
                    }
                }

                if (fci == CACHE_INDEX)
                {
                    /*
                     * This font is not in the cache.
                     */

                    #ifdef NXAGENT_FONTCACHE_DEBUG
                    fprintf(stderr, "nxagentUnrealizeFont: Freeing font not found in cache '%d'\n",
                                CACHE_ATOM(fci));
                    #endif

                    XFreeFont(nxagentDisplay, nxagentFontStruct(pFont));
                }
            }
        }

        if (nxagentFontPriv(pFont) -> mirrorID)
            FreeResource(nxagentFontPriv(pFont) -> mirrorID, RT_NONE);

        free(nxagentFontPriv(pFont));
#ifdef HAS_XFONT2
        xfont2_font_set_private(pFont, nxagentFontPrivateIndex, NULL);
#else
        FontSetPrivate(pFont, nxagentFontPrivateIndex, NULL);
#endif /* HAS_XFONT2 */
    }

    return True;
}

int nxagentDestroyNewFontResourceType(void * p, XID id)
{
    #ifdef TEST
    fprintf(stderr, "%s: Destroying mirror id [%ld] for font at [%p].\n", __func__,
                nxagentFontPriv((FontPtr) p) -> mirrorID, (void *) p);
    #endif

/*
FIXME: It happens that this resource had been already destroyed. We
       should verify if the same font is assigned both to the server
       client and another client. We had a crash when freeing server
       client resources.
*/
    if (nxagentFontPriv((FontPtr) p) != NULL)
    {
        nxagentFontPriv((FontPtr) p) -> mirrorID = None;
    }

    return 1;
}

static XFontStruct *nxagentLoadBestQueryFont(Display* dpy, char *fontName, FontPtr pFont)
{
  XFontStruct *fontStruct;

  char substFontBuf[512];;

  /*  X Logical Font Description Conventions require 14 fields in the
   *  font names.
   *
   */
  char *searchFields[FIELDS+1];
  char *fontNameFields[FIELDS+1];
  int numSearchFields = 0;
  int numFontFields = 0;
  int weight = 0;
  int tempWeight = 1;
  int fieldOrder[14] = {  4,  /* Slant       */
                         11,  /* Spacing     */
                         12,  /* Width info  */
                         13,  /* Charset     */
                         14,  /* Language    */
                          7,  /* Height      */
                          6,  /* Add-style   */
                          3,  /* Weight      */
                          2,  /* Name        */
                          1,  /* Foundry     */
                          9,  /* DPI_x       */
                          5,  /* Set-width   */
                          8,  /* Point size  */
                         10   /* DPI_y       */
                       };

  #ifdef NXAGENT_RECONNECT_FONT_DEBUG
  fprintf(stderr, "nxagentLoadBestQueryFont: Searching font '%s' .\n", fontName);
  #endif

  numFontFields = nxagentSplitString(fontName, fontNameFields, FIELDS + 1, "-");

  snprintf(substFontBuf, sizeof(substFontBuf), "%s", "fixed");

  if (numFontFields <= FIELDS)
  {
      #ifdef WARNING
      if (nxagentVerbose)
      {
          fprintf(stderr, "nxagentLoadBestQueryFont: WARNING! Font name in non standard format.\n");
      }
      #endif
  }
  else
  {
      for (int i = 1 ; i < nxagentRemoteFontList.length ; i++)
      {
          numSearchFields = nxagentSplitString(nxagentRemoteFontList.list[i]->name, searchFields, FIELDS+1, "-");


          /* The following code attempts to find an accurate approximation
           * of the missing font. The current candidate and the missing font are
           * compared on the 14 fields of the X Logical Font Description Convention.
           * The selection is performed by the analysis of the matching fields,
           * shifting left the value of the Weight variable on the right matches
           * and shifting right the value of the Weight on the wrong ones;
           * due a probability of overmuch right shifting, the starting weight is set
           * to a high value. At the end of matching the selected font is the one
           * with the bigger final Weight. The shift operation has been used instead
           * of other operation for a performance issue.
           * In some check the shift is performed by more than one position, because
           * of the relevance of the field; for example a correct slant or a matching
           * charset is more relevant than the size.
           */

          if (numSearchFields > FIELDS)
          {
            tempWeight = 0;

            for (int j = 0; j < FIELDS; j++)
            {
                if (strcasecmp(searchFields[fieldOrder[j]], fontNameFields[fieldOrder[j]]) == 0 ||
                        strcmp(searchFields[fieldOrder[j]], "") == 0 ||
                            strcmp(fontNameFields[fieldOrder[j]], "") != 0 ||
                                strcmp(searchFields[fieldOrder[j]], "*") == 0 ||
                                    strcmp(fontNameFields[fieldOrder[j]], "*") == 0)
                {
                    tempWeight ++;
                }

                tempWeight <<= 1;
            }
      }

      if (tempWeight > weight)
      {
          /* Found more accurate font  */

          weight = tempWeight;
          snprintf(substFontBuf, sizeof(substFontBuf), "%s", nxagentRemoteFontList.list[i]->name);

          #ifdef NXAGENT_RECONNECT_FONT_DEBUG
          fprintf(stderr, "nxagentLoadBestQueryFont: Weight '%d' of more accurate font '%s' .\n", weight, substFontBuf);
          #endif
      }

      for (int j = 0; j < numSearchFields; j++)
      {
          SAFE_free(searchFields[j]);
      }
    }
  }

  #ifdef WARNING
  if (nxagentVerbose)
  {
      fprintf(stderr, "nxagentLoadBestQueryFont: WARNING! Failed to load font '%s'. Replacing with '%s'.\n",
                  fontName, substFontBuf);
  }
  #endif

  fontStruct = nxagentLoadQueryFont(dpy, substFontBuf, pFont);

  for (int j = 0; j < numFontFields; j++)
  {
      SAFE_free(fontNameFields[j]);
  }

  return fontStruct;
}

static void nxagentFontDisconnect(FontPtr pFont, XID param1, void * param2)
{
  Bool *pBool = (Bool*)param2;

  if (pFont == NULL || !*pBool)
    return;

  nxagentPrivFont *privFont = nxagentFontPriv(pFont);

  #ifdef NXAGENT_RECONNECT_FONT_DEBUG
  fprintf(stderr, "nxagentFontDisconnect: pFont %p, XID %lx\n",
              (void *) pFont, privFont -> font_struct ? nxagentFont(pFont) : 0);
  #endif

  for (int i = 0; i < CACHE_INDEX; i++)
  {
      if (strcasecmp(CACHE_NAME(i), privFont -> fontName) == 0)
      {
          #ifdef NXAGENT_RECONNECT_FONT_DEBUG
          fprintf(stderr, "nxagentFontDisconnect: font %s found in cache at position %d\n",
                      privFont -> fontName, i);
          #endif

          privFont -> font_struct = NULL;
          return;
      }
  }

  #ifdef NXAGENT_RECONNECT_FONT_DEBUG
  fprintf(stderr, "nxagentFontDisconnect: WARNING font %s not found in cache freeing it now\n",
              privFont -> fontName);
  #endif

  if (privFont -> font_struct)
  {
      XFreeFont(nxagentDisplay, privFont -> font_struct);
      privFont -> font_struct = NULL;
  }
}

static void nxagentCollectFailedFont(FontPtr fpt, XID id)
{
    if (nxagentFailedToReconnectFonts.font == NULL)
    {
        nxagentFailedToReconnectFonts.size = 8;

        nxagentFailedToReconnectFonts.font = malloc(nxagentFailedToReconnectFonts.size *
                                                        sizeof(FontPtr));

        nxagentFailedToReconnectFonts.id = malloc(nxagentFailedToReconnectFonts.size *
                                                      sizeof(XID));

        if (nxagentFailedToReconnectFonts.font == NULL || nxagentFailedToReconnectFonts.id == NULL)
        {
            SAFE_free(nxagentFailedToReconnectFonts.font);
            SAFE_free(nxagentFailedToReconnectFonts.id);

            FatalError("Font: font not reconnected memory allocation failed!.\n");
        }

        #ifdef NXAGENT_RECONNECT_FONT_DEBUG
        fprintf(stderr, "nxagentCollectFailedFont: allocated [%d] bytes.\n",
                    8 * (sizeof(FontPtr)+ sizeof(XID)));
        #endif
    }
    else if (nxagentFailedToReconnectFonts.index == nxagentFailedToReconnectFonts.size - 1)
    {
        int num = 2 * nxagentFailedToReconnectFonts.size;

        FontPtr *tmp1 = realloc(nxagentFailedToReconnectFonts.font, num * sizeof(FontPtr));
        XID *tmp2 = realloc(nxagentFailedToReconnectFonts.id, num * sizeof(XID));

        if (tmp1 == NULL || tmp2 == NULL)
        {
            SAFE_free(tmp1);
            SAFE_free(tmp2);

            FatalError("Font: font not reconnected memory re-allocation failed!.\n");
        }

        nxagentFailedToReconnectFonts.size = num;
        nxagentFailedToReconnectFonts.font = tmp1;
        nxagentFailedToReconnectFonts.id = tmp2;

        #ifdef NXAGENT_RECONNECT_FONT_DEBUG
        fprintf(stderr,"nxagentCollectFailedFont: reallocated memory.\n ");
        #endif
    }

    nxagentFailedToReconnectFonts.font[nxagentFailedToReconnectFonts.index] = fpt;

    nxagentFailedToReconnectFonts.id[nxagentFailedToReconnectFonts.index] = id;

    #ifdef NXAGENT_RECONNECT_FONT_DEBUG
    fprintf(stderr, "nxagentCollectFailedFont: font not reconnected at [%p], "
                "put in nxagentFailedToReconnectFonts.font[%d] = [%p], with XID = [%lu].\n",
                (void*) fpt, nxagentFailedToReconnectFonts.index,
                (void *)nxagentFailedToReconnectFonts.font[nxagentFailedToReconnectFonts.index],
                nxagentFailedToReconnectFonts.id[nxagentFailedToReconnectFonts.index]);
    #endif

    nxagentFailedToReconnectFonts.index++;
}

static void nxagentFontReconnect(FontPtr pFont, XID param1, void * param2)
{
    int i;
    Bool *pBool = (Bool*)param2;

    if (pFont == NULL)
        return;

    nxagentPrivFont *privFont = nxagentFontPriv(pFont);

    #ifdef NXAGENT_RECONNECT_FONT_DEBUG
    fprintf(stderr, "nxagentFontReconnect: pFont %p - XID %lx - name %s\n",
                (void*) pFont, (privFont -> font_struct) ? nxagentFont(pFont) : 0,
                    privFont -> fontName);
    #endif

    for (i = 0; i < CACHE_INDEX; i++)
    {
        if (strcasecmp(CACHE_NAME(i), privFont -> fontName) == 0)
        {
            #ifdef NXAGENT_RECONNECT_FONT_DEBUG
            fprintf(stderr, "\tfound in cache");
            #endif

            if (!CACHE_FSTRUCT(i))
            {
                #ifdef NXAGENT_RECONNECT_FONT_DEBUG
                fprintf(stderr, " --- font struct not valid\n");
                #endif

               break;
            }

            nxagentFontStruct(pFont) = CACHE_FSTRUCT(i);

            return;
        }
    }

    if (i == CACHE_INDEX)
    {
        FatalError("nxagentFontReconnect: font not found in cache.");
    }

    privFont -> font_struct = nxagentLoadQueryFont(nxagentDisplay, privFont -> fontName, pFont);

    if ((privFont -> font_struct == NULL) && reconnectFlexibility)
    {
        privFont -> font_struct = nxagentLoadBestQueryFont(nxagentDisplay, privFont -> fontName, pFont);
    }

    if (privFont->font_struct != NULL)
    {
        #ifdef NXAGENT_RECONNECT_FONT_DEBUG
        fprintf(stderr, "\tXID %lx\n", privFont -> font_struct -> fid);
        #endif

        CACHE_FSTRUCT(i) = privFont -> font_struct;
    }
    else
    {
        #ifdef NXAGENT_RECONNECT_FONT_DEBUG
        fprintf(stderr, "nxagentFontReconnect: failed\n");
        #endif

        nxagentCollectFailedFont(pFont, param1);

        #ifdef NXAGENT_RECONNECT_FONT_DEBUG
        fprintf(stderr, "nxagentFontReconnect: reconnection of font [%s] failed.\n",
                    privFont -> fontName);
        #endif

        nxagentSetReconnectError(FAILED_RESUME_FONTS_ALERT,
                                     "Couldn't restore the font '%s'", privFont -> fontName);

        *pBool = False;
    }

    return;
}

static void nxagentFreeCacheBeforeReconnect(void)
{
    #ifdef NXAGENT_RECONNECT_FONT_DEBUG
    printFontCacheDump("nxagentFreeCacheBeforeReconnect");
    #endif

    for (int i = 0; i < CACHE_INDEX; i++)
    {
        if (CACHE_FSTRUCT(i))
        {
            nxagentFreeFont(CACHE_FSTRUCT(i));
            CACHE_FSTRUCT(i) = NULL;
        }
    }
}

static void nxagentCleanCacheAfterReconnect(void)
{
    int real_size = CACHE_INDEX;

    #ifdef NXAGENT_RECONNECT_FONT_DEBUG
    printFontCacheDump("nxagentCleanCacheAfterReconnect");
    #endif

    for (int i = 0; i < CACHE_INDEX; i++)
    {
        if (CACHE_FSTRUCT(i) == NULL)
        {
            SAFE_XFree(CACHE_NAME(i));
            real_size--;
        }
    }

    for (int i = 0; i < real_size; i++)
    {
        int j;
        nxCacheFontEntryRecPtr swapEntryPtr;

        /* Find - first bad occurrence if exist. */
        while ((i < real_size) && CACHE_FSTRUCT(i))
            i++;

        /* Really nothing more to do. */
        if (i == real_size)
            break;

        /*
         * Find - first good occurrence (moving backward from right end) entry in
         *        order to replace the bad one.
         */
        for (j = CACHE_INDEX - 1; CACHE_FSTRUCT(j) == NULL; j--);

        /*
         * Now we can swap the two entry and reduce the Cache index
         */
        swapEntryPtr = CACHE_ENTRY(i);
        CACHE_ENTRY(i) = CACHE_ENTRY(j);
        CACHE_ENTRY(j) = swapEntryPtr;
    }

    CACHE_INDEX = real_size;
}

#ifdef NXAGENT_RECONNECT_FONT_DEBUG
static void printFontCacheDump(char* msg)
{
    fprintf(stderr, "%s - begin -\n", msg);

    for (int i = 0; i < CACHE_INDEX; i++)
    {
        if (CACHE_FSTRUCT(i))
        {
            fprintf(stderr, "\tXID %lx - %s\n", CACHE_FSTRUCT(i) -> fid, CACHE_NAME(i));
        }
        else
        {
            fprintf(stderr, "\tdestroyed   - %s\n", CACHE_NAME(i));
        }
    }
    fprintf(stderr, "%s - end   -\n", msg);
}
#endif

Bool nxagentReconnectAllFonts(void *p0)
{
    Bool fontSuccess = True;

    reconnectFlexibility = *((int *) p0);

    #if defined(NXAGENT_RECONNECT_DEBUG) || defined(NXAGENT_RECONNECT_FONT_DEBUG)
    fprintf(stderr, "nxagentReconnectAllFonts\n");
    #endif

    /*
     * The resource type RT_NX_FONT is created on the server client
     * only, so we can avoid to loop through all the clients.
     */

    FindClientResourcesByType(clients[serverClient -> index], RT_NX_FONT,
                                  (FindResType) nxagentFontReconnect, &fontSuccess);

    for (int cid = 0; cid < MAXCLIENTS; cid++)
    {
        if (clients[cid])
        {
            FindClientResourcesByType(clients[cid], RT_FONT,
                                          (FindResType) nxagentFontReconnect, &fontSuccess);
        }
    }

    if (fontSuccess)
    {
        nxagentCleanCacheAfterReconnect();
    }

    return fontSuccess;
}

static void nxagentFailedFontReconnect(FontPtr pFont, XID param1, void * param2)
{
    int i;
    Bool *pBool = (Bool*)param2;

    if (pFont == NULL)
        return;

    nxagentPrivFont *privFont = nxagentFontPriv(pFont);

    #ifdef NXAGENT_RECONNECT_FONT_DEBUG
    fprintf(stderr, "nxagentFailedFontReconnect: pFont %p - XID %lx - name %s\n",
                (void*) pFont, (privFont -> font_struct) ? nxagentFont(pFont) : 0,
                    privFont -> fontName);
    #endif

    for (i = 0; i < CACHE_INDEX; i++)
    {
        if (strcasecmp(CACHE_NAME(i), privFont -> fontName) == 0)
        {
            #ifdef NXAGENT_RECONNECT_FONT_DEBUG
            fprintf(stderr, "\tfound in cache");
            #endif

            if (!CACHE_FSTRUCT(i))
            {
                #ifdef NXAGENT_RECONNECT_FONT_DEBUG
                fprintf(stderr, " --- font struct not valid\n");
                #endif

                break;
            }

            nxagentFontStruct(pFont) = CACHE_FSTRUCT(i);

            return;
        }
    }

    if (i == CACHE_INDEX)
    {
        FatalError("nxagentFailedFontReconnect: font not found in cache.");
    }

    privFont -> font_struct = nxagentLoadQueryFont(nxagentDisplay, privFont -> fontName, pFont);

    if (privFont -> font_struct == NULL)
    {
        privFont -> font_struct = nxagentLoadBestQueryFont(nxagentDisplay, privFont -> fontName, pFont);
    }

    if (privFont->font_struct != NULL)
    {
        #ifdef NXAGENT_RECONNECT_FONT_DEBUG
        fprintf(stderr, "\tXID %lx\n", privFont -> font_struct -> fid);
        #endif

        CACHE_FSTRUCT(i) = privFont -> font_struct;
    }
    else
    {
        #ifdef NXAGENT_RECONNECT_FONT_DEBUG
        fprintf(stderr, "nxagentFailedFontReconnect: failed\n");
        #endif

        #ifdef NXAGENT_RECONNECT_FONT_DEBUG
        fprintf(stderr, "nxagentFailedFontReconnect: reconnection of font [%s] failed.\n",
                    privFont -> fontName);
        #endif

        nxagentSetReconnectError(FAILED_RESUME_FONTS_ALERT,
                                     "Couldn't restore the font '%s'", privFont -> fontName);

        *pBool = False;
    }

    return;
}

static void nxagentFreeFailedToReconnectFonts(void)
{
    SAFE_free(nxagentFailedToReconnectFonts.font);
    SAFE_free(nxagentFailedToReconnectFonts.id);

    nxagentFailedToReconnectFonts.size = 0;
    nxagentFailedToReconnectFonts.index = 0;
}

Bool nxagentReconnectFailedFonts(void *p0)
{
    char fontServerPath[256] = "";

    reconnectFlexibility = *((int *) p0);

    #ifdef NXAGENT_RECONNECT_FONT_DEBUG
    fprintf(stderr, "nxagentReconnectFailedFonts: \n");
    #endif

    if (nxagentGetFontServerPath(fontServerPath, sizeof(fontServerPath)) == False)
    {
      #ifdef WARNING
      fprintf(stderr, "nxagentReconnectFailedFonts: WARNING! "
                  "Font server tunneling not retrieved.\n");
      #endif
    }

    #ifdef NXAGENT_RECONNECT_FONT_DEBUG
    fprintf(stderr, "nxagentReconnectFailedFonts: font server path [%s]\n", fontServerPath);
    #endif

    int nPaths = 0;
    char **fontPaths = XGetFontPath(nxagentDisplay, &nPaths);
    char **newFontPaths = malloc((nPaths + 1) * sizeof(char *));

    if (newFontPaths == NULL)
    {
        FatalError("nxagentReconnectFailedFonts: malloc failed.");
    }

    memcpy(newFontPaths, fontPaths, nPaths * sizeof(char*));

    char **localFontPaths = newFontPaths;
    localFontPaths += nPaths;
    *localFontPaths = fontServerPath;

    int attempt = 1;
    const int maxAttempt = 5;
    Bool repeat = True;
    while (repeat)
    {
        #ifdef NXAGENT_RECONNECT_FONT_DEBUG
        fprintf(stderr, "nxagentReconnectFailedFonts: attempt [%d].\n", attempt);
        #endif

        repeat = False;

        XSetFontPath(nxagentDisplay, newFontPaths, nPaths  + 1);
        nxagentFreeRemoteFontList(&nxagentRemoteFontList);
        nxagentListRemoteFonts("*", nxagentMaxFontNames);

        for (int i = 0; i < nxagentFailedToReconnectFonts.index; i++)
        {
            Bool fontSuccess = True;

            if (nxagentFailedToReconnectFonts.font[i])
            {
                nxagentFailedFontReconnect(nxagentFailedToReconnectFonts.font[i],
                                               nxagentFailedToReconnectFonts.id[i],
                                                   &fontSuccess);

                if (fontSuccess)
                {
                    nxagentFailedToReconnectFonts.font[i] = NULL;
                }
                else
                {
                    repeat = True;
                }
            }
        }

        attempt++;

        if (attempt > maxAttempt)
        {
            nxagentFreeFailedToReconnectFonts();

            XSetFontPath(nxagentDisplay, fontPaths, nPaths);
            nxagentFreeRemoteFontList(&nxagentRemoteFontList);
            nxagentListRemoteFonts("*", nxagentMaxFontNames);

            XFreeFontPath(fontPaths);
            SAFE_free(newFontPaths);

            return False;
        }
    }

    nxagentFreeFailedToReconnectFonts();

    XSetFontPath(nxagentDisplay, fontPaths, nPaths);

    XFreeFontPath(fontPaths);
    SAFE_free(newFontPaths);

    nxagentCleanCacheAfterReconnect();

    return True;
}

Bool nxagentDisconnectAllFonts(void)
{
    Bool fontSuccess = True;

    #if defined(NXAGENT_RECONNECT_DEBUG) || defined(NXAGENT_RECONNECT_FONT_DEBUG)
    fprintf(stderr, "nxagentDisconnectAllFonts\n");
    #endif

    nxagentFreeRemoteFontList(&nxagentRemoteFontList);
    nxagentFreeCacheBeforeReconnect();

    /*
     * The resource type RT_NX_FONT is created on the server client
     * only, so we can avoid to loop through all the clients.
     */

    FindClientResourcesByType(clients[serverClient -> index], RT_NX_FONT,
                                  (FindResType) nxagentFontDisconnect, &fontSuccess);

    for (int cid = 0; cid < MAXCLIENTS; cid++)
    {
        if (clients[cid] && fontSuccess)
        {
            FindClientResourcesByType(clients[cid], RT_FONT,
                                          (FindResType) nxagentFontDisconnect, &fontSuccess);
        }
    }

    return True;
}

static Bool nxagentGetFontServerPath(char * fontServerPath, int size)
{
    char path[256] = {0};

    if (NXGetFontParameters(nxagentDisplay, sizeof(path), path) == True)
    {
        /* the length is stored in the first byte and is therefore limited to 255 */
        unsigned int len = *path;

        if (len)
        {
            snprintf(fontServerPath, min(size, len + 1), "%s", path + 1);

            #ifdef TEST
            fprintf(stderr, "%s: Got path [%s].\n", __func__,
                        fontServerPath);
            #endif
        }
        else
        {
            #ifdef TEST
            fprintf(stderr, "%s: WARNING! Font server tunneling not enabled.\n", __func__);
            #endif

            return False;
        }
    }
    else
    {
        #ifdef TEST
        fprintf(stderr, "%s: WARNING! Failed to get path for font server tunneling.\n", __func__);
        #endif

        return False;
    }

    return True;
}

void nxagentVerifySingleFontPath(char **dest, const char *fontDir)
{
    if (!dest || !*dest)
        return;

    #ifdef TEST
    fprintf(stderr, "%s: Assuming fonts in directory [%s].\n", __func__,
                validateString(fontDir));
    #endif

    for (int i = 0; ; i++)
    {
        char *tmppath = NULL;
        int rc;

        const char *subdir = nxagentFontSubdirs[i];

        if (subdir == NULL)
            return;

        if (**dest != '\0')
        {
            rc = asprintf(&tmppath, "%s,%s/%s", *dest, fontDir, subdir);
        }
        else
        {
            rc = asprintf(&tmppath, "%s/%s", fontDir, subdir);
        }

        if (rc == -1)
            return;

        SAFE_free(*dest);
        *dest = tmppath;
        tmppath = NULL;
    }
}

void nxagentVerifyDefaultFontPath(void)
{
    static char *fontPath;

    #ifdef TEST
    fprintf(stderr, "%s: Going to search for one or more valid font paths.\n", __func__);
    #endif

    /*
     *  Set the default font path as the first choice.
     */

    if ((fontPath = strdup(defaultFontPath)) == NULL)
    {
        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Unable to allocate memory for a new font path. "
                    "Using the default font path [%s].\n", __func__,
                        validateString(defaultFontPath));
        #endif

        return;
    }

    for (int i = 0; ; i++)
    {
        int j;
        const char *dir = nxagentFontDirs[i];

        if (dir == NULL)
        {
            break;
        }
        else
        {
            for (j = 0; j <= i; j++)
            {
                //if (strcmp(nxagentFontDirs[j], dir) == 0)
                if (nxagentFontDirs[j] == dir)
                {
                    break;
                }
            }

            if (j == i)
            {
                nxagentVerifySingleFontPath(&fontPath, dir);
            }
#ifdef TEST
            else
            {
                fprintf(stderr, "%s: Skipping duplicate font dir [%s].\n", __func__,
                            validateString(dir));
            }
#endif
        }
    }

    if (*fontPath == '\0')
    {
        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Can't find a valid font directory.\n", __func__);
        fprintf(stderr, "%s: WARNING! Using font path [%s].\n", __func__,
                    validateString(defaultFontPath));
        #endif
    }
    else
    {
        /* do _not_ free defaultFontPath here - it's either set at compile time or
           part of argv */
        defaultFontPath = fontPath;

        #ifdef TEST
        fprintf(stderr, "%s: Using font path [%s].\n", __func__,
                    validateString(defaultFontPath));
        #endif
    }

    return;
}

XFontStruct* nxagentLoadQueryFont(register Display *dpy, char *name, FontPtr pFont)
{
    XFontStruct* fs = (XFontStruct *) malloc (sizeof (XFontStruct));

    if (fs == NULL)
    {
        #ifdef WARNING
        fprintf(stderr, "nxagentLoadQueryFont: WARNING! Failed allocation of XFontStruct.\n");
        #endif

        return (XFontStruct *)NULL;
    }

    #ifdef NXAGENT_RECONNECT_FONT_DEBUG
    fprintf(stderr, "nxagentLoadQueryFont: Looking for font '%s'.\n", name);
    #endif

    if (!nxagentFontLookUp(name))
    {
        #ifdef DEBUG
        fprintf(stderr, "nxagentLoadQueryFont: WARNING! Font not found '%s'.\n", name);
        #endif

        SAFE_free(fs);

        return (XFontStruct *) NULL;
    }

    fs -> ext_data           = NULL;                      /* Hook for extension to hang data.*/
    fs -> fid                = XLoadFont(dpy, name);      /* Font id for this font. */
    fs -> direction          = pFont->info.drawDirection; /* Hint about the direction font is painted. */
    fs -> min_char_or_byte2  = pFont->info.firstCol;      /* First character. */
    fs -> max_char_or_byte2  = pFont->info.lastCol;       /* Last character. */
    fs -> min_byte1          = pFont->info.firstRow;      /* First row that exists. */
    fs -> max_byte1          = pFont->info.lastRow;       /* Last row that exists. */
    fs -> all_chars_exist    = pFont->info.allExist;      /* Flag if all characters have nonzero size. */
    fs -> default_char       = pFont->info.defaultCh;     /* Char to print for undefined character. */
    fs -> n_properties       = pFont->info.nprops;        /* How many properties there are. */

    /*
     * If no properties defined for the font, then it is bad
     * font, but shouldn't try to read nothing.
     */

    if (fs -> n_properties > 0)
    {
        long nbytes;

        nbytes = pFont -> info.nprops * sizeof(XFontProp);
        fs -> properties = (XFontProp *) malloc((unsigned) nbytes);

        if (fs -> properties == NULL)
        {
            #ifdef WARNING
            fprintf(stderr, "nxagentLoadQueryFont: WARNING! Failed allocation of XFontProp.");
            #endif

            SAFE_free(fs);
            return (XFontStruct *) NULL;
        }

        memmove(fs -> properties, pFont -> info.props, nbytes);
    }

    xCharInfo *xcip = (xCharInfo *) &pFont -> info.ink_minbounds;

    fs -> min_bounds.lbearing      = cvtINT16toShort(xcip -> leftSideBearing);
    fs -> min_bounds.rbearing      = cvtINT16toShort(xcip -> rightSideBearing);
    fs -> min_bounds.width         = cvtINT16toShort(xcip -> characterWidth);
    fs -> min_bounds.ascent        = cvtINT16toShort(xcip -> ascent);
    fs -> min_bounds.descent       = cvtINT16toShort(xcip -> descent);
    fs -> min_bounds.attributes    = xcip -> attributes;

    xcip = (xCharInfo *) &pFont -> info.ink_maxbounds;

    fs -> max_bounds.lbearing      = cvtINT16toShort(xcip -> leftSideBearing);
    fs -> max_bounds.rbearing      = cvtINT16toShort(xcip -> rightSideBearing);
    fs -> max_bounds.width         = cvtINT16toShort(xcip -> characterWidth);
    fs -> max_bounds.ascent        = cvtINT16toShort(xcip -> ascent);
    fs -> max_bounds.descent       = cvtINT16toShort(xcip -> descent);
    fs -> max_bounds.attributes    = xcip -> attributes;

    fs -> per_char           = NULL;                              /* First_char to last_char information. */
    fs -> ascent             = pFont->info.fontAscent;            /* Logical extent above baseline for spacing. */
    fs -> descent            = pFont->info.fontDescent;           /* Logical decent below baseline for spacing. */

    return fs;
}

int nxagentFreeFont(XFontStruct *fs)
{
    if (fs->per_char)
    {
        #ifdef USE_XF86BIGFONT
        _XF86BigfontFreeFontMetrics(fs);
        #else
        SAFE_free(fs->per_char);
        #endif
    }

    SAFE_free(fs->properties);
    SAFE_XFree(fs);

    return 1;
}


int nxagentSplitString(char *string, char *fields[], int nfields, char *sep)
{
    int seplen = strlen(sep);
    int len = strlen(string);

    char *current = string;

    int i = 0;
    Bool last = False;

    for (;;)
    {
        char *next = NULL;

        if (current < string + len)
        {
            next = strstr(current, sep);
        }

        if (next == NULL)
        {
            next = string + len;
            last = True;
        }

        int fieldlen = next - current;

        if (i < nfields)
        {
            fields[i] = strndup(current, fieldlen);
        }
        else
        {
            fields[i] = NULL;
        }

        current = next + seplen;

        i++;

        if (last)
        {
            break;
        }
    }

    return i;
}

char *nxagentMakeScalableFontName(const char *fontName, Bool scalableResolution)
{
    char *scalableFontName;

    /* FIXME: use str(n)dup()? */
    if ((scalableFontName = malloc(strlen(fontName) + 1)) == NULL)
    {
        #ifdef PANIC
        fprintf(stderr, "nxagentMakeScalableFontName: PANIC! malloc() failed.\n");
        #endif

        return NULL;
    }

    scalableFontName[0] = '\0';

    if (*fontName != '-')
    {
        goto MakeScalableFontNameError;
    }

    const char *s = fontName;

    int field = 0;

    while (s != NULL)
    {
        s = strchr(s + 1, '-');

        if (s != NULL)
        {
            if (field == 6 || field == 7 || field == 11)
            {
                /*
                 * PIXEL_SIZE || POINT_SIZE || AVERAGE_WIDTH
                 */

                strcat(scalableFontName, "-0");
            }
            else if (scalableResolution && (field == 8 || field == 9))
            {
                /*
                 * RESOLUTION_X || RESOLUTION_Y
                 */

                strcat(scalableFontName, "-0");
            }
            else
            {
                strncat(scalableFontName, fontName, s - fontName);
            }

            fontName = s;
        }
        else
        {
            strcat(scalableFontName, fontName);
        }

        field++;
    }

    if (field != 14)
    {
	goto MakeScalableFontNameError;
    }

    return scalableFontName;

MakeScalableFontNameError:

    SAFE_free(scalableFontName);

    #ifdef DEBUG
    fprintf(stderr, "nxagentMakeScalableFontName: Invalid font name.\n");
    #endif

    return NULL;
}
