]> git.sesse.net Git - rdpsrv/blobdiff - Xserver/lib/font/fc/fserve.c
Import X server from vnc-3.3.7.
[rdpsrv] / Xserver / lib / font / fc / fserve.c
diff --git a/Xserver/lib/font/fc/fserve.c b/Xserver/lib/font/fc/fserve.c
new file mode 100644 (file)
index 0000000..ef9955c
--- /dev/null
@@ -0,0 +1,2824 @@
+/* $TOG: fserve.c /main/49 1997/06/10 11:23:56 barstow $ */
+/*
+
+Copyright (c) 1990  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+/* $XFree86: xc/lib/font/fc/fserve.c,v 3.4.2.2 1997/06/11 12:08:41 dawes Exp $ */
+
+/*
+ * Copyright 1990 Network Computing Devices
+ *
+ * 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, and that the names of Network Computing Devices, or Digital
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission.
+ *
+ * NETWORK COMPUTING DEVICES, AND DIGITAL AND DISCLAIM ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES,
+ * OR DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ * THIS SOFTWARE.
+ *
+ * Author:     Dave Lemke, Network Computing Devices, Inc
+ */
+/*
+ * font server specific font access
+ */
+
+#ifdef WIN32
+#define _WILLWINSOCK_
+#endif
+#include       <X11/X.h>
+#include       <X11/Xos.h>
+#include       "X11/Xpoll.h"
+#include       "FS.h"
+#include       "FSproto.h"
+#include       "fontmisc.h"
+#include       "fontstruct.h"
+#include       "fservestr.h"
+#include       <errno.h>
+#if defined(X_NOT_STDC_ENV) && !defined(__EMX__)
+extern int errno;
+#define Time_t long
+extern Time_t time ();
+#else
+#include       <time.h>
+#define Time_t time_t
+#endif
+
+#ifdef NCD
+#include       <ncd/nvram.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef MIN
+#define MIN(a,b)    ((a)<(b)?(a):(b))
+#endif
+
+#define NONZEROMETRICS(pci) ((pci)->leftSideBearing || \
+                            (pci)->rightSideBearing || \
+                            (pci)->ascent || \
+                            (pci)->descent || \
+                            (pci)->characterWidth)
+
+
+extern FontPtr find_old_font();
+
+extern int  _fs_build_range();
+
+static int  fs_read_glyphs();
+static int  fs_read_list();
+static int  fs_read_list_info();
+
+static int  fs_font_type;
+extern fd_set _fs_fd_mask;
+
+static void fs_block_handler();
+static int  fs_wakeup();
+
+static FSFpePtr awaiting_reconnect;
+
+void        _fs_connection_died();
+static int  _fs_restart_connection();
+static void _fs_try_reconnect();
+static int  fs_send_query_info();
+static int  fs_send_query_extents();
+static int  fs_send_query_bitmaps();
+static int  fs_send_close_font();
+static void fs_client_died();
+static void _fs_client_access();
+static void _fs_client_resolution();
+
+char _fs_glyph_undefined;
+char _fs_glyph_requested;
+char _fs_glyph_zero_length;
+
+/*
+ * Font server access
+ *
+ * the basic idea for the non-blocking access is to have the function
+ * called multiple times until the actual data is returned, instead
+ * of ClientBlocked.
+ *
+ * the first call to the function will cause the request to be sent to
+ * the font server, and a block record to be stored in the fpe's list
+ * of outstanding requests.  the FS block handler also sticks the
+ * proper set of fd's into the select mask.  when data is ready to be
+ * read in, the FS wakup handler will be hit.  this will read the
+ * data off the wire into the proper block record, and then signal the
+ * client that caused the block so that it can restart.  it will then
+ * call the access function again, which will realize that the data has
+ * arrived and return it.
+ */
+
+
+/* XXX this should probably be a macro once its fully debugged */
+/* ARGSUSED */
+static void
+_fs_add_req_log(conn, opcode)
+    FSFpePtr    conn;
+    int         opcode;
+{
+
+#ifdef DEBUG
+    conn->reqbuffer[conn->reqindex++] = opcode;
+    if (conn->reqindex == REQUEST_LOG_SIZE)
+       conn->reqindex = 0;
+#endif
+
+    conn->current_seq++;
+}
+
+static Bool
+fs_name_check(name)
+    char       *name;
+{
+#ifdef __EMX__
+    /* OS/2 uses D:/XFree86/.... as fontfile pathnames, so check that
+     * there is not only a protocol/ prefix, but also that the first chars
+     * are not a drive letter
+     */
+    if (name && isalpha(*name) && name[1] == ':')
+      return FALSE;
+#endif
+    /* Just make sure there is a protocol/ prefix */
+    return (name && *name != '/' && strchr(name, '/'));
+}
+
+static void
+_fs_client_resolution(conn)
+    FSFpePtr    conn;
+{
+    fsSetResolutionReq srreq;
+    int         num_res;
+    FontResolutionPtr res;
+
+    res = GetClientResolutions(&num_res);
+
+    if (num_res) {
+       srreq.reqType = FS_SetResolution;
+       srreq.num_resolutions = num_res;
+       srreq.length = (SIZEOF(fsSetResolutionReq) +
+                       (num_res * SIZEOF(fsResolution)) + 3) >> 2;
+
+       _fs_add_req_log(conn, FS_SetResolution);
+       if (_fs_write(conn, (char *) &srreq, SIZEOF(fsSetResolutionReq)) != -1)
+           (void)_fs_write_pad(conn, (char *) res,
+                               (num_res * SIZEOF(fsResolution)));
+    }
+}
+
+/*
+ * sends the stuff that's meaningful to a newly opened or reset FS
+ */
+static int
+fs_send_init_packets(conn)
+    FSFpePtr    conn;
+{
+    fsSetResolutionReq srreq;
+    fsSetCataloguesReq screq;
+    fsListCataloguesReq lcreq;
+    fsListCataloguesReply lcreply;
+    int         num_cats,
+                clen,
+                len;
+    char       *client_cat = (char *) 0,
+               *cp,
+               *sp,
+               *end;
+    int         num_res;
+    FontResolutionPtr res;
+    int         err = Successful;
+
+#define        CATALOGUE_SEP   '+'
+
+    res = GetClientResolutions(&num_res);
+    if (num_res) {
+       srreq.reqType = FS_SetResolution;
+       srreq.num_resolutions = num_res;
+       srreq.length = (SIZEOF(fsSetResolutionReq) +
+                       (num_res * SIZEOF(fsResolution)) + 3) >> 2;
+
+       _fs_add_req_log(conn, FS_SetResolution);
+       if (_fs_write(conn, (char *) &srreq, SIZEOF(fsSetResolutionReq)) == -1)
+       {
+           err = BadFontPath;
+           goto fail;
+       }
+       if (_fs_write_pad(conn, (char *) res, (num_res * SIZEOF(fsResolution))) == -1)
+       {
+           err = BadFontPath;
+           goto fail;
+       }
+    }
+    sp = strrchr(conn->servername, '/');
+
+    /* don't get tricked by a non-existant catalogue list */
+    if (sp == strchr(conn->servername, '/')) {
+       /*
+        * try original name -- this might be an alternate with no catalogues
+        */
+       sp = strrchr(conn->requestedname, '/');
+       if (sp == strchr(conn->requestedname, '/'))
+               sp = (char *) 0;
+    }
+    if (sp) {                  /* turn cats into counted list */
+       sp++;
+       /* allocate more than enough room */
+       cp = client_cat = (char *) xalloc(strlen(conn->servername));
+       if (!cp) {
+           err = BadAlloc;
+           goto fail;
+       }
+       num_cats = 0;
+       while (*sp) {
+           end = strchr(sp, CATALOGUE_SEP);
+           if (!end)
+               end = sp + strlen(sp);
+           *cp++ = len = end - sp;
+           num_cats++;
+           memmove(cp, sp, len);
+           sp += len;
+           if (*sp == CATALOGUE_SEP)
+               sp++;
+           cp += len;
+       }
+       clen = cp - client_cat;
+       /* our list checked out, so send it */
+       screq.reqType = FS_SetCatalogues;
+       screq.num_catalogues = num_cats;
+       screq.length = (SIZEOF(fsSetCataloguesReq) + clen + 3) >> 2;
+
+       _fs_add_req_log(conn, FS_SetCatalogues);
+       if (_fs_write(conn, (char *) &screq, SIZEOF(fsSetCataloguesReq)) == -1)
+       {
+           err = BadFontPath;
+           goto fail;
+       }
+       if (_fs_write_pad(conn, (char *) client_cat, clen) == -1)
+       {
+           err = BadFontPath;
+           goto fail;
+       }
+
+       /*
+        * now sync up with the font server, to see if an error was generated
+        * by a bogus catalogue
+        */
+       lcreq.reqType = FS_ListCatalogues;
+       lcreq.length = (SIZEOF(fsListCataloguesReq)) >> 2;
+       lcreq.maxNames = 0;
+       lcreq.nbytes = 0;
+       _fs_add_req_log(conn, FS_SetCatalogues);
+       if (_fs_write(conn, (char *) &lcreq, SIZEOF(fsListCataloguesReq)) == -1)
+       {
+           err = BadFontPath;
+           goto fail;
+       }
+
+       /*
+        * next bit will either by the ListCats reply, or an error followed by
+        * the reply
+        */
+       if (_fs_read(conn, (char *) &lcreply, SIZEOF(fsGenericReply)) == -1) {
+           err = BadFontPath;
+           goto fail;
+       }
+       if (lcreply.type == FS_Error &&
+               ((fsError *) & lcreply)->major_opcode == FS_SetCatalogues) {
+           _fs_eat_rest_of_error(conn, (fsError *) & lcreply);
+           /* get ListCats response */
+           (void) _fs_read(conn, (char *) &lcreply,
+                           SIZEOF(fsListCataloguesReply));
+           err = BadFontPath;
+           goto fail;
+       }
+       /* must be reply, swallow the rest of it */
+       _fs_eat_rest_of_error(conn, (fsError *) & lcreply);
+    }
+fail:
+    xfree(client_cat);
+    return err;
+}
+
+/* 
+ * close font server and remove any state associated with
+ * this connection - this includes any client records.
+ */
+
+static void
+fs_close_conn(conn)
+    FSFpePtr   conn;
+{
+    FSClientPtr        client, nclient;
+
+    /* XXX - hack.  The right fix is to remember that the font server
+       has gone away when we first discovered it. */
+    if (conn->trans_conn)
+        (void) _FontTransClose (conn->trans_conn);
+
+    if (conn->fs_fd != -1)
+      FD_CLR(conn->fs_fd, &_fs_fd_mask);
+
+    for (client = conn->clients; client; client = nclient) 
+    {
+       nclient = client->next;
+       xfree (client);
+    }
+    conn->clients = NULL;
+}
+
+/*
+ * the wakeup handlers have to be set when the FPE is open, and not
+ * removed until it is freed, in order to handle unexpected data, like
+ * events
+ */
+/* ARGSUSED */
+static int
+fs_init_fpe(fpe)
+    FontPathElementPtr fpe;
+{
+    FSFpePtr    conn;
+    char       *name;
+    int         err;
+
+    /* open font server */
+    /* create FS specific fpe info */
+    errno = 0;
+
+    name = fpe->name;
+
+    /* hack for old style names */
+    if (*name == ':')
+       name++;                 /* skip ':' */
+
+    conn = _fs_open_server(name);
+    if (conn) {
+       conn->requestedname = fpe->name; /* stash this for later init use */
+       fpe->private = (pointer) conn;
+       err = fs_send_init_packets(conn);
+       if (err != Successful) {
+           fs_close_conn(conn);
+           xfree(conn->servername);
+           xfree(conn->alts);
+           xfree(conn);
+           return err;
+       }
+       if (init_fs_handlers(fpe, fs_block_handler) != Successful)
+           return AllocError;
+       FD_SET(conn->fs_fd, &_fs_fd_mask);
+       conn->attemptReconnect = TRUE;
+
+#ifdef NCD
+       if (configData.ExtendedFontDiags)
+           printf("Connected to font server \"%s\"\n", name);
+#endif
+
+       return err;
+    }
+
+#ifdef DEBUG
+    fprintf(stderr, "failed to connect to FS \"%s\"\n", name);
+#endif
+
+#ifdef NCD
+    if (configData.ExtendedFontDiags)
+       printf("Failed to connect to font server \"%s\"\n", name);
+#endif
+
+    return (errno == ENOMEM) ? AllocError : BadFontPath;
+}
+
+static int
+fs_reset_fpe(fpe)
+    FontPathElementPtr fpe;
+{
+    (void) fs_send_init_packets((FSFpePtr) fpe->private);
+    return Successful;
+}
+
+/*
+ * this shouldn't be called till all refs to the FPE are gone
+ */
+
+static int
+fs_free_fpe(fpe)
+    FontPathElementPtr fpe;
+{
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    FSFpePtr    recon,
+               *prev;
+    prev = &awaiting_reconnect;
+    while (*prev) {
+       recon = *prev;
+       if (conn == recon) {
+           *prev = recon->next_reconnect;
+           break;
+       }
+       prev = &recon->next_reconnect;
+    }
+
+    fs_close_conn(conn);
+
+    remove_fs_handlers(fpe, fs_block_handler,
+                      !XFD_ANYSET(&_fs_fd_mask) && !awaiting_reconnect);
+
+    xfree(conn->alts);
+    xfree(conn->servername);
+    xfree(conn);
+    fpe->private = (pointer) 0;
+
+#ifdef NCD
+    if (configData.ExtendedFontDiags)
+       printf("Disconnected from font server \"%s\"\n", fpe->name);
+#endif
+
+    return Successful;
+}
+
+static      FSBlockDataPtr
+fs_new_block_rec(fpe, client, type)
+    FontPathElementPtr fpe;
+    pointer     client;
+    int         type;
+{
+    FSBlockDataPtr blockrec,
+                br;
+    FSFpePtr    fsfpe = (FSFpePtr) fpe->private;
+    int         size;
+
+    blockrec = (FSBlockDataPtr) xalloc(sizeof(FSBlockDataRec));
+    if (!blockrec)
+       return (FSBlockDataPtr) 0;
+    switch (type) {
+    case FS_OPEN_FONT:
+       size = sizeof(FSBlockedFontRec);
+       break;
+    case FS_LOAD_GLYPHS:
+       size = sizeof(FSBlockedGlyphRec);
+       break;
+    case FS_LIST_FONTS:
+       size = sizeof(FSBlockedListRec);
+       break;
+    case FS_LIST_WITH_INFO:
+       size = sizeof(FSBlockedListInfoRec);
+       break;
+    default:
+       break;
+    }
+    blockrec->data = (pointer) xalloc(size);
+    if (!blockrec->data) {
+       xfree(blockrec);
+       return (FSBlockDataPtr) 0;
+    }
+    blockrec->client = client;
+    blockrec->sequence_number = fsfpe->current_seq;
+    blockrec->type = type;
+    blockrec->depending = 0;
+    blockrec->next = (FSBlockDataPtr) 0;
+
+    /* stick it on the end of the list (since its expected last) */
+    br = (FSBlockDataPtr) fsfpe->blocked_requests;
+    if (!br) {
+       fsfpe->blocked_requests = (pointer) blockrec;
+    } else {
+       while (br->next)
+           br = br->next;
+       br->next = blockrec;
+    }
+
+    return blockrec;
+}
+
+static void
+_fs_remove_block_rec(conn, blockrec)
+    FSFpePtr    conn;
+    FSBlockDataPtr blockrec;
+{
+    FSBlockDataPtr br,
+                last;
+
+    last = (FSBlockDataPtr) 0;
+    br = (FSBlockDataPtr) conn->blocked_requests;
+    while (br) {
+       if (br == blockrec) {
+           if (last)
+               last->next = br->next;
+           else
+               conn->blocked_requests = (pointer) br->next;
+           if (br->type == FS_LOAD_GLYPHS)
+           {
+               FSBlockedGlyphPtr bglyph = (FSBlockedGlyphPtr)br->data;
+               if (bglyph->num_expected_ranges)
+                   xfree(bglyph->expected_ranges);
+           }
+           xfree(br->data);
+           xfree(br);
+           return;
+       }
+       last = br;
+       br = br->next;
+    }
+}
+
+static void
+signal_clients_depending(clients_depending)
+FSClientsDependingPtr *clients_depending;
+{
+    FSClientsDependingPtr p = *clients_depending, p2;
+    *clients_depending = (FSClientsDependingPtr)0;
+
+    while (p != (FSClientsDependingPtr)0)
+    {
+       p2 = p;
+       ClientSignal(p->client);
+       p = p->next;
+       xfree(p2);
+    }
+}
+
+static int
+add_clients_depending(clients_depending, client)
+FSClientsDependingPtr *clients_depending;
+pointer client;
+{
+    while (*clients_depending != (FSClientsDependingPtr)0)
+    {
+       if ((*clients_depending)->client == client) return Suspended;
+       clients_depending = &(*clients_depending)->next;
+    }
+    *clients_depending = (FSClientsDependingPtr)xalloc(
+                            sizeof(FSClientsDependingRec));
+    if (!*clients_depending)
+       return BadAlloc;
+
+    (*clients_depending)->client = client;
+    (*clients_depending)->next = 0;
+    return Suspended;
+}
+
+static void
+clean_aborted_blockrec(blockrec)
+    FSBlockDataPtr blockrec;
+{
+
+    switch(blockrec->type)
+    {
+       case FS_LOAD_GLYPHS:
+       {
+           FSBlockedGlyphPtr bglyph = (FSBlockedGlyphPtr)blockrec->data;
+           FontPtr pfont = bglyph->pfont;
+           int num_expected_ranges = bglyph->num_expected_ranges;
+           fsRange *expected_ranges = bglyph->expected_ranges;
+           _fs_clean_aborted_loadglyphs(pfont,
+                                    num_expected_ranges,
+                                    expected_ranges);
+           signal_clients_depending(&bglyph->clients_depending);
+           break;
+       }
+       case FS_OPEN_FONT:
+       {
+           FSBlockedFontPtr bfont = (FSBlockedFontPtr)blockrec->data;
+           signal_clients_depending(&bfont->clients_depending);
+           break;
+       }
+       default:
+           break;
+    }
+}
+
+static void
+fs_abort_blockrec(conn, blockrec)
+    FSFpePtr    conn;
+    FSBlockDataPtr blockrec;
+{
+    clean_aborted_blockrec(blockrec);
+    _fs_remove_block_rec(conn, blockrec);
+}
+
+
+static void
+fs_free_font(bfont)
+    FSBlockedFontPtr bfont;
+{
+    FontPtr     pfont;
+    FSFontDataRec *fsd;
+
+    pfont = bfont->pfont;
+    fsd = (FSFontDataRec *) pfont->fpePrivate;
+
+    /* xfree better be able to handle NULL */
+    (*pfont->unload_font)(pfont);
+    DeleteFontClientID(fsd->fontid);
+    xfree(fsd->name);
+    xfree(pfont->info.isStringProp);
+    xfree(pfont->info.props);
+
+    xfree(pfont);
+    xfree(fsd);
+
+    bfont->pfont = (FontPtr) 0;
+}
+
+static void
+_fs_cleanup_font(bfont)
+    FSBlockedFontPtr bfont;
+{
+    FSFontDataRec *fsd;
+
+    if (bfont->pfont)
+    {
+       fsd = (FSFontDataRec *) bfont->pfont->fpePrivate;
+    
+       /* make sure the FS knows we choked on it */
+       fs_send_close_font(fsd->fpe, bfont->fontid);
+    
+       fs_free_font(bfont);
+    }
+    bfont->errcode = AllocError;
+}
+
+
+static int
+fs_read_open_font(fpe, blockrec)
+    FontPathElementPtr fpe;
+    FSBlockDataPtr blockrec;
+{
+    FSBlockedFontPtr bfont = (FSBlockedFontPtr) blockrec->data;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    fsOpenBitmapFontReply rep;
+    FSBlockDataPtr blockOrig;
+    FSBlockedFontPtr origBfont;
+
+    /* pull out the OpenFont reply */
+    memcpy(&rep, &blockrec->header, SIZEOF(fsGenericReply));
+
+    if (rep.type == FS_Error) {
+       _fs_eat_rest_of_error(conn, (fsError *) & rep);
+       return BadFontName;
+    } else {                   /* get rest of reply */
+       if (_fs_read(conn, (char *) &rep + SIZEOF(fsGenericReply),
+             SIZEOF(fsOpenBitmapFontReply) - SIZEOF(fsGenericReply)) == -1) {
+           /* If we're not reopening a font, we'll allocate the
+              structures again after connection is reestablished.  */
+           if (!(bfont->flags & FontReopen)) fs_free_font(bfont);
+           return StillWorking;
+       }
+    }
+
+    /* If we're not reopening a font and FS detected a duplicate font
+       open request, replace our reference to the new font with a
+       reference to an existing font (possibly one not finished
+       opening).  If this is a reopen, keep the new font reference...
+       it's got the metrics and extents we read when the font was opened
+       before.  This also gives us the freedom to easily close the font
+       if we we decide (in fs_read_query_info()) that we don't like what
+       we got. */
+
+    if (rep.otherid && !(bfont->flags & FontReopen)) {
+       (void) fs_send_close_font(fpe, bfont->fontid);
+
+       /* Find old font if we're completely done getting it from server. */
+       fs_free_font(bfont);
+       bfont->pfont = find_old_font(rep.otherid);
+       bfont->fontid = rep.otherid;
+       bfont->state = FS_DONE_REPLY;
+       /*
+        * look for a blocked request to open the same font
+        */
+       for (blockOrig = (FSBlockDataPtr) conn->blocked_requests;
+               blockOrig;
+               blockOrig = blockOrig->next) {
+           if (blockOrig != blockrec && blockOrig->type == FS_OPEN_FONT) {
+               origBfont = (FSBlockedFontPtr) blockOrig->data;
+               if (origBfont->fontid == rep.otherid) {
+                   blockrec->depending = blockOrig->depending;
+                   blockOrig->depending = blockrec;
+                   bfont->state = FS_DEPENDING;
+                   bfont->pfont = origBfont->pfont;
+                   break;
+               }
+           }
+       }
+       if (bfont->pfont == NULL)
+       {
+           /* XXX - something nasty happened */
+           return BadFontName;
+       }
+       return AccessDone;
+    }
+
+    bfont->pfont->info.cachable = rep.cachable != 0;
+    bfont->state = FS_INFO_REPLY;
+    /* ask for the next stage */
+    (void) fs_send_query_info(fpe, blockrec);
+    return StillWorking;
+}
+
+
+static int
+fs_read_query_info(fpe, blockrec)
+    FontPathElementPtr fpe;
+    FSBlockDataPtr blockrec;
+{
+    FSBlockedFontPtr bfont = (FSBlockedFontPtr) blockrec->data;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    fsQueryXInfoReply rep;
+    fsPropInfo  pi;
+    fsPropOffset *po;
+    pointer     pd;
+    unsigned long prop_len;
+    FSBlockedFontRec newbfont, *oldbfont;
+    FontRec newpfont, *oldpfont;
+    int err;
+
+    /* If this is a reopen, accumulate the query info into a dummy
+       font and compare to our original data. */
+    if (bfont->flags & FontReopen)
+    {
+       newbfont = *(oldbfont = bfont);
+       bfont = &newbfont;
+       newpfont = *(oldpfont = oldbfont->pfont);
+       newpfont.info.isStringProp = NULL;
+       newpfont.info.props = NULL;
+       newbfont.pfont = &newpfont;
+       err = StillWorking;
+    }
+
+    /* pull out the QueryXInfo reply */
+    memcpy(&rep, &blockrec->header, SIZEOF(fsGenericReply));
+    if (_fs_read(conn, (char *) &rep + SIZEOF(fsGenericReply),
+                SIZEOF(fsQueryXInfoReply) - SIZEOF(fsGenericReply)) == -1) {
+       if (bfont->flags & FontReopen) goto bail;
+       fs_free_font(bfont);
+       return StillWorking;
+    }
+    /* move the data over */
+    fsUnpack_XFontInfoHeader(&rep, &bfont->pfont->info);
+    _fs_init_fontinfo(conn, &bfont->pfont->info);
+
+    if (bfont->pfont->info.terminalFont)
+    {
+       bfont->format =
+           (bfont->format & ~ (BitmapFormatImageRectMask)) |
+           BitmapFormatImageRectMax;
+    }
+
+    if (_fs_read(conn, (char *) &pi, SIZEOF(fsPropInfo)) == -1) {
+       if (bfont->flags & FontReopen) goto bail;
+       fs_free_font(bfont);
+       return StillWorking;
+    }
+    prop_len = pi.num_offsets * SIZEOF(fsPropOffset);
+    po = (fsPropOffset *) xalloc(prop_len);
+    pd = (pointer) xalloc(pi.data_len);
+    if (!po || !pd) {
+       xfree(pd);
+       xfree(po);
+       /* clear the wire */
+       (void) _fs_drain_bytes(conn, prop_len + pi.data_len);
+       /* clean up the font */
+       if (bfont->flags & FontReopen) { err = AllocError ; goto bail; }
+       (void) _fs_cleanup_font(bfont);
+       return AllocError;
+    }
+    if (_fs_read_pad(conn, (char *) po, prop_len) == -1 ||
+           _fs_read_pad(conn, (char *) pd, pi.data_len) == -1) {
+       xfree(pd);
+       xfree(po);
+       if (bfont->flags & FontReopen) goto bail;
+       fs_free_font(bfont);
+       return StillWorking;
+    }
+    if (_fs_convert_props(&pi, po, pd, &bfont->pfont->info) == -1)
+    {
+       xfree(po);
+       xfree(pd);
+       if (bfont->flags & FontReopen) { err = AllocError ; goto bail; }
+       (void) _fs_cleanup_font(bfont);
+       return AllocError;
+    }
+    xfree(po);
+    xfree(pd);
+
+    if (bfont->flags & FontReopen)
+    {
+       int i;
+
+       err = BadFontName;
+
+       /* We're reopening a font that we lost because of a downed
+          connection.  In the interest of avoiding corruption from
+          opening a different font than the old one (we already have
+          its metrics, extents, and probably some of its glyphs),
+          verify that the metrics and properties all match.  */
+
+       if (newpfont.info.firstCol != oldpfont->info.firstCol ||
+           newpfont.info.lastCol != oldpfont->info.lastCol ||
+           newpfont.info.firstRow != oldpfont->info.firstRow ||
+           newpfont.info.lastRow != oldpfont->info.lastRow ||
+           newpfont.info.defaultCh != oldpfont->info.defaultCh ||
+           newpfont.info.noOverlap != oldpfont->info.noOverlap ||
+           newpfont.info.terminalFont != oldpfont->info.terminalFont ||
+           newpfont.info.constantMetrics != oldpfont->info.constantMetrics ||
+           newpfont.info.constantWidth != oldpfont->info.constantWidth ||
+           newpfont.info.inkInside != oldpfont->info.inkInside ||
+           newpfont.info.inkMetrics != oldpfont->info.inkMetrics ||
+           newpfont.info.allExist != oldpfont->info.allExist ||
+           newpfont.info.drawDirection != oldpfont->info.drawDirection ||
+           newpfont.info.cachable != oldpfont->info.cachable ||
+           newpfont.info.anamorphic != oldpfont->info.anamorphic ||
+           newpfont.info.maxOverlap != oldpfont->info.maxOverlap ||
+           newpfont.info.fontAscent != oldpfont->info.fontAscent ||
+           newpfont.info.fontDescent != oldpfont->info.fontDescent ||
+           newpfont.info.nprops != oldpfont->info.nprops)
+           goto bail;
+
+#define MATCH(xci1, xci2) \
+       (((xci1).leftSideBearing == (xci2).leftSideBearing) && \
+        ((xci1).rightSideBearing == (xci2).rightSideBearing) && \
+        ((xci1).characterWidth == (xci2).characterWidth) && \
+        ((xci1).ascent == (xci2).ascent) && \
+        ((xci1).descent == (xci2).descent) && \
+        ((xci1).attributes == (xci2).attributes))
+
+       if (!MATCH(newpfont.info.maxbounds, oldpfont->info.maxbounds) ||
+           !MATCH(newpfont.info.minbounds, oldpfont->info.minbounds) ||
+           !MATCH(newpfont.info.ink_maxbounds, oldpfont->info.ink_maxbounds) ||
+           !MATCH(newpfont.info.ink_minbounds, oldpfont->info.ink_minbounds))
+           goto bail;
+
+#undef MATCH
+
+       for (i = 0; i < newpfont.info.nprops; i++)
+           if (newpfont.info.isStringProp[i] !=
+                   oldpfont->info.isStringProp[i] ||
+               newpfont.info.props[i].name !=
+                   oldpfont->info.props[i].name ||
+               newpfont.info.props[i].value !=
+                   oldpfont->info.props[i].value)
+               goto bail;
+
+       err = Successful;
+    bail:
+       if (err != Successful && err != StillWorking)
+       {
+           /* Failure.  Close the font. */
+           fs_send_close_font(((FSFontDataPtr)oldpfont->fpePrivate)->fpe,
+                              bfont->fontid);
+           ((FSFontDataPtr)oldpfont->fpePrivate)->generation  = -1;
+       }
+       xfree(newpfont.info.isStringProp);
+       xfree(newpfont.info.props);
+
+       if (err == Successful) oldbfont->state = FS_DONE_REPLY;
+       return err;
+    }
+
+    if (glyphCachingMode == CACHING_OFF ||
+       glyphCachingMode == CACHE_16_BIT_GLYPHS && !bfont->pfont->info.lastRow)
+       bfont->flags |= FontLoadAll;
+
+    bfont->state = FS_EXTENT_REPLY;
+
+    fs_send_query_extents(fpe, blockrec);
+    return StillWorking;
+}
+
+static int
+fs_read_extent_info(fpe, blockrec)
+    FontPathElementPtr fpe;
+    FSBlockDataPtr blockrec;
+{
+    FSBlockedFontPtr bfont = (FSBlockedFontPtr) blockrec->data;
+    FSFontDataPtr fsd = (FSFontDataPtr) bfont->pfont->fpePrivate;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    fsQueryXExtents16Reply rep;
+    int         i;
+    int                numInfos;
+    Bool       haveInk = FALSE; /* need separate ink metrics? */
+    CharInfoPtr ci,
+                pCI;
+    FSFontPtr   fsfont = (FSFontPtr) bfont->pfont->fontPrivate;
+    fsXCharInfo *fsci;
+    fsXCharInfo fscilocal;
+    pointer fscip;
+
+    /* read the QueryXExtents reply */
+    memcpy(&rep, &blockrec->header, SIZEOF(fsGenericReply));
+    if (_fs_read(conn, (char *) &rep + SIZEOF(fsGenericReply),
+             SIZEOF(fsQueryXExtents16Reply) - SIZEOF(fsGenericReply)) == -1) {
+       fs_free_font(bfont);
+       return StillWorking;
+    }
+    /* move the data over */
+    /* need separate inkMetrics for fixed font server protocol version */
+    numInfos =  rep.num_extents;
+    if (bfont->pfont->info.terminalFont && conn->fsMajorVersion > 1)
+    {
+       numInfos *= 2;
+       haveInk = TRUE;
+    }
+    ci = pCI = (CharInfoPtr) xalloc(sizeof(CharInfoRec) * numInfos);
+/* XXX this could be done with an ALLOCATE_LOCAL */
+    fsci = (fsXCharInfo *) xalloc(SIZEOF(fsXCharInfo) * rep.num_extents);
+    if (!pCI || !fsci) {
+       xfree(pCI);
+       xfree(fsci);
+       /* clear the unusable data */
+       _fs_drain_bytes(conn, SIZEOF(fsXCharInfo) * rep.num_extents);
+       _fs_cleanup_font(bfont);
+       return AllocError;
+    }
+    fsfont->encoding = pCI;
+    if (haveInk)
+       fsfont->inkMetrics = pCI + rep.num_extents;
+    else
+        fsfont->inkMetrics = pCI;
+
+    if (_fs_read_pad(conn, (char *) fsci,
+                    SIZEOF(fsXCharInfo) * rep.num_extents) == -1) {
+       fs_free_font(bfont);
+       xfree(fsci);
+       return StillWorking;
+    }
+    fsd->glyphs_to_get = 0;
+    fscip = (pointer) fsci;
+    ci = fsfont->inkMetrics;
+    for (i = 0; i < rep.num_extents; i++) {
+       memcpy(&fscilocal, fscip, SIZEOF(fsXCharInfo)); /* align it */
+       _fs_convert_char_info(&fscilocal, &ci->metrics);
+       fscip += SIZEOF(fsXCharInfo);
+       /* Initialize the bits field for later glyph-caching use */
+       if (NONZEROMETRICS(&ci->metrics))
+       {
+           if (!haveInk &&
+               (ci->metrics.leftSideBearing == ci->metrics.rightSideBearing ||
+                ci->metrics.ascent == -ci->metrics.descent))
+               pCI[i].bits = &_fs_glyph_zero_length;
+           else
+           {
+               pCI[i].bits = &_fs_glyph_undefined;
+               fsd->glyphs_to_get++;
+           }
+       }
+       else
+           pCI[i].bits = (char *)0;
+       ci++;
+    }
+
+    xfree(fsci);
+
+    /* build bitmap metrics, ImageRectMax style */
+    if (haveInk)
+    {
+       FontInfoRec *fi = &bfont->pfont->info;
+       CharInfoPtr ii;
+
+       ci = fsfont->encoding;
+       ii = fsfont->inkMetrics;
+       for (i = 0; i < rep.num_extents; i++, ci++, ii++)
+       {
+           if (NONZEROMETRICS(&ii->metrics))
+           {
+               ci->metrics.leftSideBearing = FONT_MIN_LEFT(fi);
+               ci->metrics.rightSideBearing = FONT_MAX_RIGHT(fi);
+               ci->metrics.ascent = FONT_MAX_ASCENT(fi);
+               ci->metrics.descent = FONT_MAX_DESCENT(fi);
+               ci->metrics.characterWidth = FONT_MAX_WIDTH(fi);
+               ci->metrics.attributes = ii->metrics.attributes;
+           }
+           else
+           {
+               ci->metrics = ii->metrics;
+           }
+       }
+    }
+    {
+       unsigned int r, c, numCols, firstCol;
+
+       firstCol = bfont->pfont->info.firstCol;
+       numCols = bfont->pfont->info.lastCol - firstCol + 1;
+       c = bfont->pfont->info.defaultCh;
+       fsfont->pDefault = 0;
+       if (bfont->pfont->info.lastRow)
+       {
+           r = c >> 8;
+           r -= bfont->pfont->info.firstRow;
+           c &= 0xff;
+           c -= firstCol;
+           if (r < bfont->pfont->info.lastRow-bfont->pfont->info.firstRow+1 &&
+               c < numCols)
+               fsfont->pDefault = &pCI[r * numCols + c];
+       }
+       else
+       {
+           c -= firstCol;
+           if (c < numCols)
+               fsfont->pDefault = &pCI[c];
+       }
+    }
+    bfont->state = FS_GLYPHS_REPLY;
+
+    if (bfont->flags & FontLoadBitmaps) {
+       fs_send_query_bitmaps(fpe, blockrec);
+       return StillWorking;
+    }
+    return Successful;
+}
+
+/*
+ * XXX should probably continue to read here if we can, but must be sure
+ * it's our packet waiting, rather than another interspersed
+ */
+static int
+fs_do_open_font(fpe, blockrec, readheader)
+    FontPathElementPtr fpe;
+    FSBlockDataPtr blockrec;
+    Bool        readheader;
+{
+    FSBlockedFontPtr bfont = (FSBlockedFontPtr) blockrec->data;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    int         err;
+
+    switch (bfont->state) {
+    case FS_OPEN_REPLY:
+       if (readheader) {
+           /* get the next header */
+           if (_fs_read(conn, (char *) &blockrec->header,
+                        SIZEOF(fsGenericReply)) == -1) {
+               fs_free_font(bfont);
+               err = StillWorking;
+               break;
+           }
+       }
+       bfont->errcode = fs_read_open_font(fpe, blockrec);
+       if (bfont->errcode != StillWorking) {   /* already loaded, or error */
+           /* if font's already loaded, massage error code */
+           switch (bfont->state) {
+           case FS_DONE_REPLY:
+               bfont->errcode = Successful;
+               break;
+           case FS_DEPENDING:
+               bfont->errcode = StillWorking;
+               break;
+           }
+           err = bfont->errcode;
+           break;
+       }
+       /* if more data to read or Sync, fall thru, else return */
+       if (!(bfont->flags & FontOpenSync)) {
+           err = bfont->errcode;
+           break;
+       } else {
+           if (_fs_read(conn, (char *) &blockrec->header,
+                        SIZEOF(fsGenericReply)) == -1) {
+               fs_free_font(bfont);
+               err = StillWorking;
+               break;
+           }
+       }
+       /* fall through */
+    case FS_INFO_REPLY:
+       bfont->errcode = fs_read_query_info(fpe, blockrec);
+       if (bfont->errcode != StillWorking) {
+           err = bfont->errcode;
+           break;
+       }
+       if (!(bfont->flags & FontOpenSync)) {
+           err = bfont->errcode;
+           break;
+           /* if more data to read, fall thru, else return */
+       } else {
+           if (_fs_read(conn, (char *) &blockrec->header,
+                        SIZEOF(fsGenericReply))) {
+               fs_free_font(bfont);
+               err = StillWorking;
+               break;
+           }
+       }
+       /* fall through */
+    case FS_EXTENT_REPLY:
+       bfont->errcode = fs_read_extent_info(fpe, blockrec);
+       if (bfont->errcode != StillWorking) {
+           err = bfont->errcode;
+           break;
+       }
+       if (!(bfont->flags & FontOpenSync)) {
+           err = bfont->errcode;
+           break;
+       } else if (bfont->flags & FontLoadBitmaps) {
+           if (_fs_read(conn, (char *) &blockrec->header,
+                        SIZEOF(fsGenericReply))) {
+               fs_free_font(bfont);
+               err = StillWorking;
+               break;
+           }
+       }
+       /* fall through */
+    case FS_GLYPHS_REPLY:
+       if (bfont->flags & FontLoadBitmaps) {
+           bfont->errcode = fs_read_glyphs(fpe, blockrec);
+       }
+       err = bfont->errcode;
+       break;
+    case FS_DEPENDING:         /* can't happen */
+       err = bfont->errcode;
+    default:
+       err = bfont->errcode;
+       break;
+    }
+    if (err != StillWorking) {
+       bfont->state = FS_DONE_REPLY;   /* for _fs_load_glyphs() */
+       while (blockrec = blockrec->depending) {
+           bfont = (FSBlockedFontPtr) blockrec->data;
+           bfont->errcode = err;
+           bfont->state = FS_DONE_REPLY;       /* for _fs_load_glyphs() */
+       }
+    }
+    return err;
+}
+
+/* ARGSUSED */
+static void
+fs_block_handler(data, wt, LastSelectMask)
+    pointer     data;
+    struct timeval **wt;
+    fd_set*      LastSelectMask;
+{
+    static struct timeval recon_timeout;
+    Time_t      now,
+                soonest;
+    FSFpePtr    recon;
+
+    XFD_ORSET(LastSelectMask, LastSelectMask, &_fs_fd_mask);
+    if (recon = awaiting_reconnect) {
+       now = time((Time_t *) 0);
+       soonest = recon->time_to_try;
+       while (recon = recon->next_reconnect) {
+           if (recon->time_to_try < soonest)
+               soonest = recon->time_to_try;
+       }
+       if (soonest < now)
+           soonest = now;
+       soonest = soonest - now;
+       recon_timeout.tv_sec = soonest;
+       recon_timeout.tv_usec = 0;
+       if (*wt == (struct timeval *) 0) {
+           *wt = &recon_timeout;
+       } else if ((*wt)->tv_sec > soonest) {
+           **wt = recon_timeout;
+       }
+    }
+}
+
+static void
+fs_handle_unexpected(conn, rep)
+    FSFpePtr    conn;
+    fsGenericReply *rep;
+{
+    if (rep->type == FS_Event && rep->data1 == KeepAlive) {
+       fsNoopReq   req;
+
+       /* ping it back */
+       req.reqType = FS_Noop;
+       req.length = SIZEOF(fsNoopReq) >> 2;
+       _fs_add_req_log(conn, FS_Noop);
+       _fs_write(conn, (char *) &req, SIZEOF(fsNoopReq));
+    }
+    /* this should suck up unexpected replies and events */
+    _fs_eat_rest_of_error(conn, (fsError *) rep);
+}
+
+static int
+fs_wakeup(fpe, LastSelectMask)
+    FontPathElementPtr fpe;
+    fd_set* LastSelectMask;
+{
+    FSBlockDataPtr blockrec,
+                br;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    int         err;
+    fsGenericReply rep;
+
+    if (awaiting_reconnect) {
+       _fs_try_reconnect();
+    }
+    /* see if there's any data to be read */
+
+    /* 
+     * Don't continue if the fd is -1 (which will be true when the
+     * font server terminates
+     */
+    if (conn->fs_fd == -1)
+       return FALSE;
+
+    if (FD_ISSET(conn->fs_fd, LastSelectMask)) {
+
+#if defined(NOTDEF) || defined(__EMX__)                /* bogus - doesn't deal with EOF very well,
+                                * now does it ... */
+       /*
+        * make sure it isn't spurious - mouse events seem to trigger extra
+        * problems. Under OS/2, this is especially true ...
+        */
+       if (_fs_data_ready(conn) <= 0) {
+           return FALSE;
+       }
+#endif
+
+       /* get the header */
+       if (_fs_read(conn, (char *) &rep, SIZEOF(fsGenericReply)) == -1)
+           return FALSE;
+
+       /* find the matching block record */
+
+       for (br = (FSBlockDataPtr) conn->blocked_requests; br; br = br->next) {
+           if ((CARD16)(br->sequence_number & 0xffff) ==
+               (CARD16)(rep.sequenceNumber - 1))
+               break;
+       }
+       if (!br) {
+           fs_handle_unexpected(conn, &rep);
+           return FALSE;
+       }
+       blockrec = br;
+
+       memcpy(&blockrec->header, &rep, SIZEOF(fsGenericReply));
+
+       /* go read it, and if we're done, wake up the appropriate client */
+       switch (blockrec->type) {
+       case FS_OPEN_FONT:
+           err = fs_do_open_font(fpe, blockrec, FALSE);
+           break;
+       case FS_LOAD_GLYPHS:
+           err = fs_read_glyphs(fpe, blockrec);
+           break;
+       case FS_LIST_FONTS:
+           err = fs_read_list(fpe, blockrec);
+           break;
+       case FS_LIST_WITH_INFO:
+           err = fs_read_list_info(fpe, blockrec);
+           break;
+       default:
+           break;
+       }
+
+       if (err != StillWorking) {
+           while (blockrec) {
+               ClientSignal(blockrec->client);
+               blockrec = blockrec->depending;
+           }
+       }
+       /*
+        * Xx we could loop here and eat any additional replies, but it should
+        * feel more responsive for other clients if we come back later
+        */
+    } else if (awaiting_reconnect) {
+       _fs_try_reconnect();
+    }
+    return FALSE;
+}
+
+/*
+ * Reconnection code
+ */
+
+void
+_fs_connection_died(conn)
+    FSFpePtr    conn;
+{
+    if (!conn->attemptReconnect)
+       return;
+    conn->attemptReconnect = FALSE;
+    fs_close_conn(conn);
+    conn->time_to_try = time((Time_t *) 0) + FS_RECONNECT_WAIT;
+    conn->reconnect_delay = FS_RECONNECT_WAIT;
+    conn->fs_fd = -1;
+    conn->trans_conn = NULL;
+    conn->next_reconnect = awaiting_reconnect;
+    awaiting_reconnect = conn;
+}
+
+static int
+_fs_restart_connection(conn)
+    FSFpePtr    conn;
+{
+    FSBlockDataPtr block;
+
+    conn->current_seq = 0;
+    FD_SET(conn->fs_fd, &_fs_fd_mask);
+    if (!fs_send_init_packets(conn))
+       return FALSE;
+    while (block = (FSBlockDataPtr) conn->blocked_requests) {
+       ClientSignal(block->client);
+       fs_abort_blockrec(conn, block);
+    }
+    return TRUE;
+}
+
+static void
+_fs_try_reconnect()
+{
+    FSFpePtr    conn,
+               *prev;
+    Time_t      now;
+
+    prev = &awaiting_reconnect;
+    now = time((Time_t *) 0);
+    while (conn = *prev) {
+       if (now - conn->time_to_try > 0) {
+           if (_fs_reopen_server(conn) && _fs_restart_connection(conn)) {
+               conn->attemptReconnect = TRUE;
+               *prev = conn->next_reconnect;
+               if (prev == &awaiting_reconnect) continue;
+           } else {
+               if (conn->reconnect_delay < FS_MAX_RECONNECT_WAIT)
+                   conn->reconnect_delay *= 2;
+               now = time((Time_t *) 0);
+               conn->time_to_try = now + conn->reconnect_delay;
+           }
+       }
+       prev = &conn->next_reconnect;
+    }
+}
+
+/*
+ * sends the actual request out
+ */
+/* ARGSUSED */
+static int
+fs_send_open_font(client, fpe, flags, name, namelen, format, fmask, id, ppfont)
+    pointer     client;
+    FontPathElementPtr fpe;
+    Mask        flags;
+    char       *name;
+    int         namelen;
+    fsBitmapFormat format;
+    fsBitmapFormatMask fmask;
+    XID         id;
+    FontPtr    *ppfont;
+{
+    FontPtr     newfont;
+    FSBlockDataPtr blockrec = NULL;
+    FSBlockedFontPtr blockedfont;
+    FSFontDataPtr fsd;
+    FSFontPtr   fsfont;
+    FSFpePtr    conn;
+    fsOpenBitmapFontReq openreq;
+    int         err = Suspended;
+    XID         newid;
+    unsigned char buf[1024];
+    char       *fontname;
+
+    if (flags & FontReopen)
+    {
+       Atom nameatom, fn = None;
+       int i;
+
+       newfont = *ppfont;
+       fsd = (FSFontDataPtr)newfont->fpePrivate;
+       fsfont = (FSFontPtr)newfont->fontPrivate;
+       fpe = newfont->fpe;
+       format = fsd->format;
+       fmask = fsd->fmask;
+       newid = fsd->fontid;
+       /* This is an attempt to reopen a font.  Did the font have a
+          NAME property? */
+       if ((nameatom = MakeAtom("FONT", 4, 0)) != None)
+       {
+           for (i = 0; i < newfont->info.nprops; i++)
+               if (newfont->info.props[i].name == nameatom &&
+                   newfont->info.isStringProp[i])
+               {
+                   fn = newfont->info.props[i].value;
+                   break;
+               }
+       }
+       if (fn == None || !(name = NameForAtom(fn)))
+       {
+           name = fsd->name;
+           namelen = fsd->namelen;
+       }
+       else
+           namelen = strlen(name);
+    }
+
+    conn = (FSFpePtr) fpe->private;
+    if (namelen > sizeof (buf) - 1)
+       return BadFontName;
+    _fs_client_access (conn, client, (flags & FontOpenSync) != 0);
+    _fs_client_resolution(conn);
+
+
+    if (!(flags & FontReopen))
+    {
+
+       newid = GetNewFontClientID();
+
+       /* make the font */
+       newfont = (FontPtr) xalloc(sizeof(FontRec));
+
+       /* and the FS data */
+       fsd = (FSFontDataPtr) xalloc(sizeof(FSFontDataRec));
+
+       fsfont = (FSFontPtr) xalloc(sizeof(FSFontRec));
+
+       fontname = (char *)xalloc(namelen);
+
+       if (!newfont || !fsd || !fsfont || !fontname) {
+lowmem:
+           if (!(flags & FontReopen))
+           {
+               xfree((char *) newfont);
+               xfree((char *) fsd);
+               xfree((char *) fsfont);
+               xfree((char *) fontname);
+           }
+           if (blockrec) fs_abort_blockrec(conn, blockrec);
+           return AllocError;
+       }
+       bzero((char *) newfont, sizeof(FontRec));
+       bzero((char *) fsfont, sizeof(FSFontRec));
+       bzero((char *) fsd, sizeof(FSFontDataRec));
+    }
+
+    /* make a new block record, and add it to the end of the list */
+    blockrec = fs_new_block_rec(fpe, client, FS_OPEN_FONT);
+    if (!blockrec) {
+       goto lowmem;
+    }
+
+    if (!(flags & FontReopen))
+    {
+       int bit, byte, scan, glyph;
+
+       newfont->refcnt = 0;
+       newfont->maxPrivate = -1;
+       newfont->devPrivates = (pointer *) 0;
+       newfont->format = format;
+
+       /* These font components will be needed in packGlyphs */
+       CheckFSFormat(format, BitmapFormatMaskBit |
+                             BitmapFormatMaskByte |
+                             BitmapFormatMaskScanLineUnit |
+                             BitmapFormatMaskScanLinePad,
+                     &bit,
+                     &byte,
+                     &scan,
+                     &glyph,
+                     NULL);
+       newfont->bit = bit;
+       newfont->byte = byte;
+       newfont->scan = scan;
+       newfont->glyph = glyph;
+
+       newfont->fpe = fpe;
+       newfont->fpePrivate = (pointer) fsd;
+       newfont->fontPrivate = (pointer) fsfont;
+       _fs_init_font(newfont);
+
+       fsd->fpe = fpe;
+       fsd->name = fontname;
+       fsd->namelen = namelen;
+       memcpy(fontname, name, namelen);
+       fsd->format = format;
+       fsd->fmask = fmask;
+    }
+    fsd->fontid = newid;
+    fsd->generation = conn->generation;
+
+    blockedfont = (FSBlockedFontPtr) blockrec->data;
+    blockedfont->fontid = newid;
+    blockedfont->pfont = newfont;
+    blockedfont->state = FS_OPEN_REPLY;
+    blockedfont->flags = flags;
+    blockedfont->format = format;
+    blockedfont->clients_depending = (FSClientsDependingPtr)0;
+
+    /* save the ID */
+    if (!StoreFontClientFont(blockedfont->pfont, blockedfont->fontid)) {
+       goto lowmem;
+    }
+    /* do an FS_OpenFont, FS_QueryXInfo and FS_QueryXExtents */
+    buf[0] = (unsigned char) namelen;
+    memcpy(&buf[1], name, namelen);
+    namelen++;
+    openreq.reqType = FS_OpenBitmapFont;
+    openreq.fid = newid;
+    openreq.format_hint = format;
+    openreq.format_mask = fmask;
+    openreq.length = (SIZEOF(fsOpenBitmapFontReq) + namelen + 3) >> 2;
+
+    _fs_add_req_log(conn, FS_OpenBitmapFont);
+    _fs_write(conn, (char *) &openreq, SIZEOF(fsOpenBitmapFontReq));
+    _fs_write_pad(conn, (char *) buf, namelen);
+
+#ifdef NCD
+    if (configData.ExtendedFontDiags) {
+       memcpy(buf, name, MIN(256, namelen));
+       buf[MIN(256, namelen)] = '\0';
+       printf("Requesting font \"%s\" from font server \"%s\"\n",
+              buf, fpe->name);
+    }
+#endif
+
+    if (flags & FontOpenSync) {
+       err = fs_do_open_font(fpe, blockrec, TRUE);
+       if (blockedfont->errcode == Successful) {
+           *ppfont = blockedfont->pfont;
+       } else {
+           _fs_cleanup_font(blockedfont);
+       }
+       _fs_remove_block_rec(conn, blockrec);
+    }
+    return err;
+}
+
+static int
+fs_send_query_info(fpe, blockrec)
+    FontPathElementPtr fpe;
+    FSBlockDataPtr blockrec;
+{
+    FSBlockedFontPtr bfont;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    fsQueryXInfoReq inforeq;
+
+    bfont = (FSBlockedFontPtr) blockrec->data;
+
+    inforeq.reqType = FS_QueryXInfo;
+    inforeq.id = bfont->fontid;
+    inforeq.length = SIZEOF(fsQueryXInfoReq) >> 2;
+
+    blockrec->sequence_number = conn->current_seq;
+    _fs_add_req_log(conn, FS_QueryXInfo);
+    _fs_write(conn, (char *) &inforeq, SIZEOF(fsQueryXInfoReq));
+
+    return Successful;
+}
+
+static int
+fs_send_query_extents(fpe, blockrec)
+    FontPathElementPtr fpe;
+    FSBlockDataPtr blockrec;
+{
+    FSBlockedFontPtr bfont;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    fsQueryXExtents16Req extreq;
+
+    bfont = (FSBlockedFontPtr) blockrec->data;
+
+    extreq.reqType = FS_QueryXExtents16;
+    extreq.range = fsTrue;
+    extreq.fid = bfont->fontid;
+    extreq.num_ranges = 0;
+    extreq.length = SIZEOF(fsQueryXExtents16Req) >> 2;
+
+    blockrec->sequence_number = conn->current_seq;
+    _fs_add_req_log(conn, FS_QueryXExtents16);
+    _fs_write(conn, (char *) &extreq, SIZEOF(fsQueryXExtents16Req));
+
+    return Successful;
+}
+
+static int
+fs_send_query_bitmaps(fpe, blockrec)
+    FontPathElementPtr fpe;
+    FSBlockDataPtr blockrec;
+{
+    FSBlockedFontPtr bfont;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    fsQueryXBitmaps16Req bitreq;
+
+
+    bfont = (FSBlockedFontPtr) blockrec->data;
+
+    /* send the request */
+    bitreq.reqType = FS_QueryXBitmaps16;
+    bitreq.fid = bfont->fontid;
+    bitreq.format = bfont->format;
+    bitreq.range = TRUE;
+    bitreq.length = SIZEOF(fsQueryXBitmaps16Req) >> 2;
+    bitreq.num_ranges = 0;
+
+    blockrec->sequence_number = conn->current_seq;
+    _fs_add_req_log(conn, FS_QueryXBitmaps16);
+    _fs_write(conn, (char *) &bitreq, SIZEOF(fsQueryXBitmaps16Req));
+
+    return Successful;
+}
+
+/* ARGSUSED */
+static int
+fs_open_font(client, fpe, flags, name, namelen, format, fmask, id, ppfont,
+            alias, non_cachable_font)
+    pointer     client;
+    FontPathElementPtr fpe;
+    Mask        flags;
+    char       *name;
+    fsBitmapFormat format;
+    fsBitmapFormatMask fmask;
+    int         namelen;
+    XID         id;
+    FontPtr    *ppfont;
+    char      **alias;
+    FontPtr     non_cachable_font;     /* Not used in this FPE */
+{
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    FSBlockDataPtr blockrec;
+    FSBlockedFontPtr blockedfont;
+    int         err;
+
+    /* libfont interface expects ImageRectMin glyphs */
+    format = format & ~BitmapFormatImageRectMask | BitmapFormatImageRectMin;
+
+    *alias = (char *) 0;
+    /* XX if we find the blockrec for the font */
+    blockrec = (FSBlockDataPtr) conn->blocked_requests;
+    while (blockrec != (FSBlockDataPtr) 0) {
+       if (blockrec->type == FS_OPEN_FONT &&
+               blockrec->client == client) {
+           blockedfont = (FSBlockedFontPtr) blockrec->data;
+           err = blockedfont->errcode;
+           if (err == Successful) {
+               *ppfont = blockedfont->pfont;
+           } else {
+               _fs_cleanup_font(blockedfont);
+           }
+           /* cleanup */
+           _fs_remove_block_rec(conn, blockrec);
+           return err;
+       }
+       blockrec = blockrec->next;
+    }
+    return fs_send_open_font(client, fpe, flags, name, namelen, format, fmask,
+                            id, ppfont);
+}
+
+/* ARGSUSED */
+static int
+fs_send_close_font(fpe, id)
+    FontPathElementPtr fpe;
+    Font        id;
+{
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    fsCloseReq  req;
+
+    /* tell the font server to close the font */
+    req.reqType = FS_CloseFont;
+    req.length = SIZEOF(fsCloseReq) >> 2;
+    req.id = id;
+    _fs_add_req_log(conn, FS_CloseFont);
+    _fs_write(conn, (char *) &req, SIZEOF(fsCloseReq));
+
+    return Successful;
+}
+
+/* ARGSUSED */
+static int
+fs_close_font(fpe, pfont)
+    FontPathElementPtr fpe;
+    FontPtr     pfont;
+{
+    FSFontDataPtr fsd = (FSFontDataPtr) pfont->fpePrivate;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+
+    /* XXX we may get called after the resource DB has been cleaned out */
+    if (find_old_font(fsd->fontid))
+       DeleteFontClientID(fsd->fontid);
+    if (conn->generation == fsd->generation)
+       fs_send_close_font(fpe, fsd->fontid);
+    (*pfont->unload_font) (pfont);
+
+
+    xfree(fsd->name);
+    xfree(fsd);
+    xfree(pfont->info.isStringProp);
+    xfree(pfont->info.props);
+    xfree(pfont->devPrivates);
+    xfree(pfont);
+
+
+    return Successful;
+}
+
+static int
+fs_read_glyphs(fpe, blockrec)
+    FontPathElementPtr fpe;
+    FSBlockDataPtr blockrec;
+{
+    FSBlockedGlyphPtr bglyph = (FSBlockedGlyphPtr) blockrec->data;
+    FSBlockedFontPtr bfont = (FSBlockedFontPtr) blockrec->data;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    FontPtr pfont = bglyph->pfont;      /* works for either blocked font
+                                          or glyph rec...  pfont is at
+                                          the very beginning of both
+                                          blockrec->data structures */
+    FSFontDataPtr fsd = (FSFontDataPtr) (pfont->fpePrivate);
+    FSFontPtr   fsdata = (FSFontPtr) pfont->fontPrivate;
+    FontInfoPtr        pfi = &pfont->info;
+    fsQueryXBitmaps16Reply rep;
+    fsOffset32   *ppbits;
+    fsOffset32 local_off;
+    char       *off_adr;
+    pointer     pbitmaps;
+    char       *bits;
+    int         glyph_size,
+                offset_size,
+                i,
+               err;
+    int                nranges = 0;
+    fsRange    *ranges, *nextrange;
+    unsigned long minchar, maxchar;
+
+    /* get reply header */
+    memcpy(&rep, &blockrec->header, SIZEOF(fsGenericReply));
+    if (rep.type == FS_Error) {
+/* XXX -- translate FS error */
+       _fs_eat_rest_of_error(conn, (fsError *) & rep);
+       err = AllocError;
+       goto bail;
+    }
+    if (_fs_read(conn, (char *) &rep + SIZEOF(fsGenericReply),
+             SIZEOF(fsQueryXBitmaps16Reply) - SIZEOF(fsGenericReply)) == -1) {
+       if (blockrec->type == FS_OPEN_FONT)
+           fs_free_font(bfont);
+       return StillWorking;
+    }
+    /* allocate space for glyphs */
+    offset_size = SIZEOF(fsOffset32) * (rep.num_chars);
+    glyph_size = (rep.length << 2) - SIZEOF(fsQueryXBitmaps16Reply)
+       - offset_size;
+    ppbits = (fsOffset32 *) xalloc(offset_size);
+    pbitmaps = (pointer) xalloc(glyph_size);
+    if (glyph_size && !pbitmaps || !ppbits)
+    {
+       xfree(pbitmaps);
+       xfree(ppbits);
+
+       /* clear wire */
+       (void) _fs_drain_bytes_pad(conn, offset_size);
+       (void) _fs_drain_bytes_pad(conn, glyph_size);
+
+       if (blockrec->type == FS_OPEN_FONT)
+           _fs_cleanup_font(bfont);
+       err = AllocError;
+       goto bail;
+    }
+
+    /* read offsets */
+    if (_fs_read_pad(conn, (char *) ppbits, offset_size) == -1) {
+       if (blockrec->type == FS_OPEN_FONT)
+           fs_free_font(bfont);
+       return StillWorking;
+    }
+
+    /* read glyphs */
+    if (_fs_read_pad(conn, (char *) pbitmaps, glyph_size) == -1) {
+       if (blockrec->type == FS_OPEN_FONT)
+           fs_free_font(bfont);
+       return StillWorking;
+    }
+
+    if (blockrec->type == FS_LOAD_GLYPHS)
+    {
+       nranges = bglyph->num_expected_ranges;
+       nextrange = ranges = bglyph->expected_ranges;
+    }
+
+    /* place the incoming glyphs */
+    if (nranges)
+    {
+       /* We're operating under the assumption that the ranges
+          requested in the LoadGlyphs call were all legal for this
+          font, and that individual ranges do not cover multiple
+          rows...  fs_build_range() is designed to ensure this. */
+       minchar = (nextrange->min_char_high - pfi->firstRow) *
+                 (pfi->lastCol - pfi->firstCol + 1) +
+                 nextrange->min_char_low - pfi->firstCol;
+       maxchar = (nextrange->max_char_high - pfi->firstRow) *
+                 (pfi->lastCol - pfi->firstCol + 1) +
+                 nextrange->max_char_low - pfi->firstCol;
+       nextrange++;
+    }
+    else
+    {
+       minchar = 0;
+       maxchar = rep.num_chars;
+    }
+
+    off_adr = (char *)ppbits;
+    for (i = 0; i < rep.num_chars; i++)
+    {
+       memcpy(&local_off, off_adr, SIZEOF(fsOffset32));        /* align it */
+       if (blockrec->type == FS_OPEN_FONT ||
+           fsdata->encoding[minchar].bits == &_fs_glyph_requested)
+       {
+           if (local_off.length)
+           {
+               bits = (char *)xalloc(local_off.length);
+               if (bits == NULL)
+               {
+                   xfree(ppbits);
+                   xfree(pbitmaps);
+                   err = AllocError;
+                   goto bail;
+               }
+               memcpy(bits, pbitmaps + local_off.position,
+                      local_off.length);
+           }
+           else if (NONZEROMETRICS(&fsdata->encoding[minchar].metrics))
+               bits = &_fs_glyph_zero_length;
+           else
+               bits = 0;
+           if (fsdata->encoding[minchar].bits == &_fs_glyph_requested)
+               fsd->glyphs_to_get--;
+           fsdata->encoding[minchar].bits = bits;
+       }
+       if (minchar++ == maxchar)
+       {
+           if (!--nranges) break;
+           minchar = (nextrange->min_char_high - pfi->firstRow) *
+                     (pfi->lastCol - pfi->firstCol + 1) +
+                     nextrange->min_char_low - pfi->firstCol;
+           maxchar = (nextrange->max_char_high - pfi->firstRow) *
+                     (pfi->lastCol - pfi->firstCol + 1) +
+                     nextrange->max_char_low - pfi->firstCol;
+           nextrange++;
+       }
+       off_adr += SIZEOF(fsOffset32);
+    }
+
+    xfree(ppbits);
+    xfree(pbitmaps);
+
+    if (blockrec->type == FS_OPEN_FONT)
+    {
+       fsd->glyphs_to_get = 0;
+       bfont->state = FS_DONE_REPLY;
+    }
+    err = Successful;
+
+bail:
+    if (blockrec->type == FS_LOAD_GLYPHS)
+    {
+       bglyph->done = TRUE;
+       bglyph->errcode = err;
+    }
+
+    return err;
+}
+
+
+
+static int
+fs_send_load_glyphs(client, pfont, nranges, ranges)
+    pointer     client;
+    FontPtr     pfont;
+    int                nranges;
+    fsRange    *ranges;
+{
+    FSBlockedGlyphPtr blockedglyph;
+    fsQueryXBitmaps16Req req;
+    FSFontDataPtr fsd = (FSFontDataPtr) (pfont->fpePrivate);
+    FontPathElementPtr fpe = fsd->fpe;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    FSBlockDataPtr blockrec;
+
+    /* make a new block record, and add it to the end of the list */
+    blockrec = fs_new_block_rec(fpe, client, FS_LOAD_GLYPHS);
+    if (!blockrec)
+       return AllocError;
+    blockedglyph = (FSBlockedGlyphPtr) blockrec->data;
+    blockedglyph->pfont = pfont;
+    blockedglyph->num_expected_ranges = nranges;
+    /* Assumption: it's our job to free ranges */
+    blockedglyph->expected_ranges = ranges;
+    blockedglyph->done = FALSE;
+    blockedglyph->clients_depending = (FSClientsDependingPtr)0;
+
+    blockrec->sequence_number = conn->current_seq;
+
+    /* send the request */
+    req.reqType = FS_QueryXBitmaps16;
+    req.fid = ((FSFontDataPtr) pfont->fpePrivate)->fontid;
+    req.format = pfont->format;
+    if (pfont->info.terminalFont)
+       req.format = req.format & ~(BitmapFormatImageRectMask) |
+                    BitmapFormatImageRectMax;
+    req.range = TRUE;
+    /* each range takes up 4 bytes */
+    req.length = (SIZEOF(fsQueryXBitmaps16Req) >> 2) + nranges;
+    req.num_ranges = nranges * 2;      /* protocol wants count of fsChar2bs */
+    _fs_add_req_log(conn, FS_QueryXBitmaps16);
+    _fs_write(conn, (char *) &req, SIZEOF(fsQueryXBitmaps16Req));
+
+    /* Send ranges to the server... pack into a char array by hand
+       to avoid structure-packing portability problems and to
+       handle swapping for version1 protocol */
+    if (nranges)
+    {
+#define RANGE_BUFFER_SIZE 64
+#define RANGE_BUFFER_SIZE_MASK 63
+       int i;
+       char range_buffer[RANGE_BUFFER_SIZE * 4];
+       char *range_buffer_p;
+
+       range_buffer_p = range_buffer;
+       for (i = 0; i < nranges;)
+       {
+           if (conn->fsMajorVersion > 1)
+           {
+               *range_buffer_p++ = ranges[i].min_char_high;
+               *range_buffer_p++ = ranges[i].min_char_low;
+               *range_buffer_p++ = ranges[i].max_char_high;
+               *range_buffer_p++ = ranges[i].max_char_low;
+           }
+           else
+           {
+               *range_buffer_p++ = ranges[i].min_char_low;
+               *range_buffer_p++ = ranges[i].min_char_high;
+               *range_buffer_p++ = ranges[i].max_char_low;
+               *range_buffer_p++ = ranges[i].max_char_high;
+           }
+
+           if (!(++i & RANGE_BUFFER_SIZE_MASK))
+           {
+               _fs_write(conn, range_buffer, RANGE_BUFFER_SIZE * 4);
+               range_buffer_p = range_buffer;
+           }
+       }
+       if (i &= RANGE_BUFFER_SIZE_MASK)
+           _fs_write(conn, range_buffer, i * 4);
+    }
+
+    return Suspended;
+}
+
+
+int
+fs_load_all_glyphs(pfont)
+    FontPtr    pfont;
+{
+    extern pointer serverClient;       /* This could be any number that
+                                          doesn't conflict with existing
+                                          client values. */
+    int err;
+    FSFpePtr conn = (FSFpePtr) pfont->fpe->private;
+
+    /*
+     * The purpose of this procedure is to load all glyphs in the event
+     * that we're dealing with someone who doesn't understand the finer
+     * points of glyph caching...  it is called from _fs_get_glyphs() if
+     * the latter is called to get glyphs that have not yet been loaded.
+     * We assume that the caller will not know how to handle a return
+     * value of Suspended (usually the case for a GetGlyphs() caller),
+     * so this procedure hangs around, freezing the server, for the
+     * request to complete.  This is an unpleasant kluge called to
+     * perform an unpleasant job that, we hope, will never be required.
+     */
+
+    while ((err = _fs_load_glyphs(serverClient, pfont, TRUE, 0, 0, NULL)) ==
+          Suspended)
+    {
+       fd_set TempSelectMask;
+       if (_fs_wait_for_readable(conn) == -1)
+       {
+           /* We lost our connection.  Don't wait to reestablish it;
+              just give up. */
+           _fs_connection_died(conn);
+
+           /* Get rid of blockrec */
+           fs_client_died(serverClient, pfont->fpe);
+
+           return BadCharRange;        /* As good an error as any other */
+       }
+       FD_SET(conn->fs_fd, &TempSelectMask);
+       fs_wakeup(pfont->fpe, &TempSelectMask);
+    }
+
+    return err;
+}
+
+
+int
+_fs_load_glyphs(client, pfont, range_flag, nchars, item_size, data)
+    pointer     client;
+    FontPtr     pfont;
+    Bool       range_flag;
+    unsigned int nchars;
+    int         item_size;
+    unsigned char *data;
+{
+
+    int                nranges = 0;
+    fsRange     *ranges = NULL;
+    int         res;
+    FSBlockDataPtr blockrec;
+    FSBlockedGlyphPtr blockedglyph;
+    FSFpePtr    conn = (FSFpePtr) pfont->fpe->private;
+    FSClientsDependingPtr *clients_depending = NULL;
+
+    /* see if the result is already there */
+
+    blockrec = (FSBlockDataPtr) conn->blocked_requests;
+    while (blockrec) {
+       if (blockrec->type == FS_LOAD_GLYPHS)
+       {
+           blockedglyph = (FSBlockedGlyphPtr) blockrec->data;
+           if (blockedglyph->pfont == pfont)
+           {
+               if (blockrec->client == client)
+               {
+                   if (blockedglyph->done)
+                   {
+                       int errcode = blockedglyph->errcode;
+                       signal_clients_depending(&blockedglyph->
+                                                clients_depending);
+                       _fs_remove_block_rec(conn, blockrec);
+                       return errcode;
+                   }
+                   else return Suspended;
+               }
+               /* We've found an existing LoadGlyphs blockrec for this
+                  font but for another client.  Rather than build a
+                  blockrec for it now (which entails some complex
+                  maintenance), we'll add it to a queue of clients to
+                  be signalled when the existing LoadGlyphs is
+                  completed.  */
+               clients_depending = &blockedglyph->clients_depending;
+               break;
+           }
+       }
+       else if (blockrec->type == FS_OPEN_FONT)
+       {
+           FSBlockedFontPtr bfont;
+           bfont = (FSBlockedFontPtr) blockrec->data;
+           if (bfont->pfont == pfont)
+           {
+               if (blockrec->client == client)
+               {
+                   if (bfont->state == FS_DONE_REPLY)
+                   {
+                       int errcode = bfont->errcode;
+                       signal_clients_depending(&bfont->clients_depending);
+                       _fs_remove_block_rec(conn, blockrec);
+                       if (errcode == Successful) break;
+                       else return errcode;
+                   }
+                   else return Suspended;
+               }
+               /* We've found an existing OpenFont blockrec for this
+                  font but for another client.  Rather than build a
+                  blockrec for it now (which entails some complex
+                  maintenance), we'll add it to a queue of clients to
+                  be signalled when the existing OpenFont is
+                  completed.  */
+               if (bfont->state != FS_DONE_REPLY)
+               {
+                   clients_depending = &bfont->clients_depending;
+                   break;
+               }
+           }
+       }
+                
+       blockrec = blockrec->next;
+    }
+
+    /*
+     * see if the desired glyphs already exist, and return Successful if they
+     * do, otherwise build up character range/character string
+     */
+    res = fs_build_range(pfont, range_flag, nchars, item_size, data,
+                        &nranges, &ranges);
+
+    switch (res)
+    {
+       case AccessDone:
+           return Successful;
+
+       case Successful:
+           break;
+
+       default:
+           return res;
+    }
+
+    /*
+     * If clients_depending is not null, this request must wait for
+     * some prior request(s) to complete.
+     */
+    if (clients_depending)
+    {
+       /* Since we're not ready to send the load_glyphs request yet,
+          clean up the damage (if any) caused by the fs_build_range()
+          call. */
+       if (nranges)
+       {
+           _fs_clean_aborted_loadglyphs(pfont, nranges, ranges);
+           xfree(ranges);
+       }
+       return add_clients_depending(clients_depending, client);
+    }
+
+    /*
+     * If fsd->generation != conn->generation, the font has been closed
+     * due to a lost connection.  We will reopen it, which will result
+     * in one of three things happening:
+     *  1) The open will succeed and obtain the same font.  Life
+     *     is wonderful.
+     *  2) The open will fail.  There is code above to recognize this
+     *     and flunk the LoadGlyphs request.  The client might not be
+     *     thrilled.
+     *  3) Worst case: the open will succeed but the font we open will
+     *     be different.  The fs_read_query_info() procedure attempts
+     *     to detect this by comparing the existing metrics and
+     *     properties against those of the reopened font... if they
+     *     don't match, we flunk the reopen, which eventually results
+     *     in flunking the LoadGlyphs request.  We could go a step
+     *     further and compare the extents, but this should be
+     *     sufficient.
+     */
+    if (((FSFontDataPtr)pfont->fpePrivate)->generation != conn->generation)
+    {
+       /* Since we're not ready to send the load_glyphs request yet,
+          clean up the damage caused by the fs_build_range() call. */
+       _fs_clean_aborted_loadglyphs(pfont, nranges, ranges);
+       xfree(ranges);
+
+       /* Now try to reopen the font. */
+       return fs_send_open_font(client, (FontPathElementPtr)0,
+                                (Mask)FontReopen, (char *)0, 0,
+                                (fsBitmapFormat)0, (fsBitmapFormatMask)0,
+                                (XID)0, &pfont);
+    }
+
+    return fs_send_load_glyphs(client, pfont, nranges, ranges);
+}
+
+
+
+static int
+fs_read_list(fpe, blockrec)
+    FontPathElementPtr fpe;
+    FSBlockDataPtr blockrec;
+{
+    FSBlockedListPtr blist = (FSBlockedListPtr) blockrec->data;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    fsListFontsReply rep;
+    char       *data,
+               *dp;
+    int         length,
+                i;
+
+    blist->done = TRUE;
+
+    /* read reply header */
+    memcpy(&rep, &blockrec->header, SIZEOF(fsGenericReply));
+    if (rep.type == FS_Error) {
+/* XXX -- translate FS error */
+       _fs_eat_rest_of_error(conn, (fsError *) & rep);
+       return AllocError;
+    }
+    if (_fs_read(conn, (char *) &rep + SIZEOF(fsGenericReply),
+                SIZEOF(fsListFontsReply) - SIZEOF(fsGenericReply)) == -1) {
+       /* nothing to free (i think) */
+       return StillWorking;
+    }
+    length = (rep.length << 2) - SIZEOF(fsListFontsReply);
+    data = (char *) xalloc(length);
+    if (!data) {
+       _fs_drain_bytes_pad(conn, length);
+       return AllocError;
+    }
+    /* read the list */
+    if (_fs_read_pad(conn, data, length) == -1) {
+       /* nothing to free (i think) */
+       return StillWorking;
+    }
+    /* copy data into FontPathRecord */
+    dp = data;
+    for (i = 0; i < rep.nFonts; i++) {
+       length = *(unsigned char *)dp++;
+       if (AddFontNamesName(blist->names, dp, length) != Successful) {
+           blist->errcode = AllocError;
+           break;
+       }
+       dp += length;
+    }
+
+    xfree(data);
+    return Successful;
+}
+
+static int
+fs_send_list_fonts(client, fpe, pattern, patlen, maxnames, newnames)
+    pointer     client;
+    FontPathElementPtr fpe;
+    char       *pattern;
+    int         patlen;
+    int         maxnames;
+    FontNamesPtr newnames;
+{
+    FSBlockDataPtr blockrec;
+    FSBlockedListPtr blockedlist;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    fsListFontsReq req;
+
+    _fs_client_access (conn, client, FALSE);
+    _fs_client_resolution(conn);
+
+    /* make a new block record, and add it to the end of the list */
+    blockrec = fs_new_block_rec(fpe, client, FS_LIST_FONTS);
+    if (!blockrec)
+       return AllocError;
+    blockedlist = (FSBlockedListPtr) blockrec->data;
+    blockedlist->patlen = patlen;
+    blockedlist->errcode = Successful;
+    blockedlist->names = newnames;
+    blockedlist->done = FALSE;
+
+    /* send the request */
+    req.reqType = FS_ListFonts;
+    req.maxNames = maxnames;
+    req.nbytes = patlen;
+    req.length = (SIZEOF(fsListFontsReq) + patlen + 3) >> 2;
+    _fs_add_req_log(conn, FS_ListFonts);
+    _fs_write(conn, (char *) &req, SIZEOF(fsListFontsReq));
+    _fs_write_pad(conn, (char *) pattern, patlen);
+
+#ifdef NCD
+    if (configData.ExtendedFontDiags) {
+       char        buf[256];
+
+       memcpy(buf, pattern, MIN(256, patlen));
+       buf[MIN(256, patlen)] = '\0';
+       printf("Listing fonts on pattern \"%s\" from font server \"%s\"\n",
+              buf, fpe->name);
+    }
+#endif
+
+    return Suspended;
+}
+
+static int
+fs_list_fonts(client, fpe, pattern, patlen, maxnames, newnames)
+    pointer     client;
+    FontPathElementPtr fpe;
+    char       *pattern;
+    int         patlen;
+    int         maxnames;
+    FontNamesPtr newnames;
+{
+    FSBlockDataPtr blockrec;
+    FSBlockedListPtr blockedlist;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    int         err;
+
+    /* see if the result is already there */
+    blockrec = (FSBlockDataPtr) conn->blocked_requests;
+    while (blockrec) {
+       if (blockrec->type == FS_LIST_FONTS && blockrec->client == client) {
+           blockedlist = (FSBlockedListPtr) blockrec->data;
+           if (blockedlist->patlen == patlen && blockedlist->done) {
+               err = blockedlist->errcode;
+               _fs_remove_block_rec(conn, blockrec);
+               return err;
+           }
+       }
+       blockrec = blockrec->next;
+    }
+
+    /* didn't find waiting record, so send a new one */
+    return fs_send_list_fonts(client, fpe, pattern, patlen, maxnames, newnames);
+}
+
+static int  padlength[4] = {0, 3, 2, 1};
+
+static int
+fs_read_list_info(fpe, blockrec)
+    FontPathElementPtr fpe;
+    FSBlockDataPtr blockrec;
+{
+    FSBlockedListInfoPtr binfo = (FSBlockedListInfoPtr) blockrec->data;
+    fsListFontsWithXInfoReply rep;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    fsPropInfo  pi;
+    fsPropOffset *po;
+    char       *name;
+    pointer     pd;
+    int                err;
+
+    /* clean up anything from the last trip */
+    if (binfo->name)
+    {
+       xfree(binfo->name);
+       binfo->name = NULL;
+    }
+    if (binfo->pfi) {
+       xfree(binfo->pfi->isStringProp);
+       xfree(binfo->pfi->props);
+       xfree(binfo->pfi);
+       binfo->pfi = NULL;
+    }
+    /* get reply header */
+    memcpy(&rep, &blockrec->header, SIZEOF(fsGenericReply));
+    if (rep.type == FS_Error) {
+/* XXX -- translate FS error */
+       _fs_eat_rest_of_error(conn, (fsError *) & rep);
+       binfo->errcode = AllocError;
+       return AllocError;
+    }
+    if (conn->fsMajorVersion > 1)
+       if (rep.nameLength == 0)
+           goto done;
+    /* old protocol sent a full-length reply even for the last one */
+    if (_fs_read(conn, (char *) &rep + SIZEOF(fsGenericReply),
+         SIZEOF(fsListFontsWithXInfoReply) - SIZEOF(fsGenericReply)) == -1) {
+       goto done;
+    }
+    if (rep.nameLength == 0)
+       goto done;
+
+    /* read the data */
+    name = (char *) xalloc(rep.nameLength);
+    binfo->pfi = (FontInfoPtr) xalloc(sizeof(FontInfoRec));
+    if (!name || !binfo->pfi) {
+       xfree(name);
+       xfree(binfo->pfi);
+       binfo->pfi = NULL;
+       _fs_drain_bytes(conn,
+                       rep.length - (SIZEOF(fsListFontsWithXInfoReply) -
+                                     SIZEOF(fsGenericReply)));
+       binfo->errcode = AllocError;
+       return AllocError;
+    }
+    if (conn->fsMajorVersion == 1)
+       if (_fs_read_pad(conn, name, rep.nameLength) == -1)
+           goto done;
+    if (_fs_read_pad(conn, (char *) &pi, SIZEOF(fsPropInfo)) == -1)
+           goto done;
+
+    po = (fsPropOffset *) xalloc(SIZEOF(fsPropOffset) * pi.num_offsets);
+    pd = (pointer) xalloc(pi.data_len);
+    if (!po || !pd) {
+       xfree(name);
+       xfree(po);
+       xfree(pd);
+       xfree (binfo->pfi);
+       binfo->pfi = NULL;
+       binfo->errcode = AllocError;
+       return AllocError;
+    }
+    err = _fs_read_pad(conn, (char *) po,
+                      (pi.num_offsets * SIZEOF(fsPropOffset)));
+    if (err != -1)
+    {
+       if (conn->fsMajorVersion > 1)
+           err = _fs_read(conn, (char *) pd, pi.data_len);
+       else
+           err = _fs_read_pad(conn, (char *) pd, pi.data_len);
+    }
+    if (err != -1  &&  conn->fsMajorVersion != 1)
+    {
+       err = _fs_read(conn, name, rep.nameLength);
+       if (err != -1)
+           err = _fs_drain_bytes(conn, padlength[(pi.data_len+rep.nameLength)&3]);
+    }
+
+    if (err == -1) {
+       xfree(name);
+       xfree(po);
+       xfree(pd);
+       xfree (binfo->pfi);
+       binfo->pfi = NULL;
+       goto done;
+    }
+
+    if (_fs_convert_lfwi_reply(conn, binfo->pfi, &rep, &pi, po, pd) != Successful)
+    {
+       xfree(name);
+       xfree(po);
+       xfree(pd);
+       xfree (binfo->pfi);
+       binfo->pfi = NULL;
+       goto done;
+    }
+    xfree(po);
+    xfree(pd);
+    binfo->name = name;
+    binfo->namelen = rep.nameLength;
+    binfo->remaining = rep.nReplies;
+
+    binfo->status = FS_LFWI_REPLY;
+    binfo->errcode = Suspended;
+    /* disable this font server until we've processed this response */
+    FD_CLR(conn->fs_fd, &_fs_fd_mask);
+
+    return Successful;
+
+done:
+    binfo->status = FS_LFWI_FINISHED;
+    binfo->errcode = BadFontName;
+    binfo->name = (char *) 0;
+    return Successful;
+}
+
+/* ARGSUSED */
+static int
+fs_start_list_with_info(client, fpe, pattern, len, maxnames, pdata)
+    pointer     client;
+    FontPathElementPtr fpe;
+    char       *pattern;
+    int         len;
+    int         maxnames;
+    pointer    *pdata;
+{
+    FSBlockDataPtr blockrec;
+    FSBlockedListInfoPtr blockedinfo;
+    fsListFontsWithXInfoReq req;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+
+    _fs_client_access (conn, client, FALSE);
+    _fs_client_resolution(conn);
+
+    /* make a new block record, and add it to the end of the list */
+    blockrec = fs_new_block_rec(fpe, client, FS_LIST_WITH_INFO);
+    if (!blockrec)
+       return AllocError;
+    blockedinfo = (FSBlockedListInfoPtr) blockrec->data;
+    bzero((char *) blockedinfo, sizeof(FSBlockedListInfoRec));
+    blockedinfo->status = FS_LFWI_WAITING;
+    blockedinfo->errcode = Suspended;
+
+    /* send the request */
+    req.reqType = FS_ListFontsWithXInfo;
+    req.maxNames = maxnames;
+    req.nbytes = len;
+    req.length = (SIZEOF(fsListFontsWithXInfoReq) + len + 3) >> 2;
+    _fs_add_req_log(conn, FS_ListFontsWithXInfo);
+    (void) _fs_write(conn, (char *) &req, SIZEOF(fsListFontsWithXInfoReq));
+    (void) _fs_write_pad(conn, pattern, len);
+
+#ifdef NCD
+    if (configData.ExtendedFontDiags) {
+       char        buf[256];
+
+       memcpy(buf, pattern, MIN(256, len));
+       buf[MIN(256, len)] = '\0';
+       printf("Listing fonts with info on pattern \"%s\" from font server \"%s\"\n",
+              buf, fpe->name);
+    }
+#endif
+
+    return Successful;
+}
+
+/* ARGSUSED */
+static int
+fs_next_list_with_info(client, fpe, namep, namelenp, pFontInfo, numFonts,
+                      private)
+    pointer     client;
+    FontPathElementPtr fpe;
+    char      **namep;
+    int        *namelenp;
+    FontInfoPtr *pFontInfo;
+    int        *numFonts;
+    pointer     private;
+{
+    FSBlockDataPtr blockrec;
+    FSBlockedListInfoPtr blockedinfo;
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+
+    /* see if the result is already there */
+    blockrec = (FSBlockDataPtr) conn->blocked_requests;
+    while (blockrec) {
+       if (blockrec->type == FS_LIST_WITH_INFO &&
+               blockrec->client == client) {
+           blockedinfo = (FSBlockedListInfoPtr) blockrec->data;
+           break;
+       }
+       blockrec = blockrec->next;
+    }
+
+    if (!blockrec)
+    {
+       /* The only good reason for not finding a blockrec would be if
+          disconnect/reconnect to the font server wiped it out and the
+          code that called us didn't do the right thing to create
+          another one.  Under those circumstances, we need to return an
+          error to prevent that code from attempting to interpret the
+          information we don't return.  */
+       return BadFontName;
+    }
+
+    if (blockedinfo->status == FS_LFWI_WAITING)
+       return Suspended;
+
+    *namep = blockedinfo->name;
+    *namelenp = blockedinfo->namelen;
+    *pFontInfo = blockedinfo->pfi;
+    *numFonts = blockedinfo->remaining;
+    FD_SET(conn->fs_fd, &_fs_fd_mask);
+    if (blockedinfo->status == FS_LFWI_FINISHED) {
+       int         err = blockedinfo->errcode;
+
+       _fs_remove_block_rec(conn, blockrec);
+       return err;
+    }
+    if (blockedinfo->status == FS_LFWI_REPLY) {
+       blockedinfo->status = FS_LFWI_WAITING;
+       return Successful;
+    } else {
+       return blockedinfo->errcode;
+    }
+}
+
+/*
+ * Called when client exits
+ */
+
+static void
+fs_client_died(client, fpe)
+    pointer     client;
+    FontPathElementPtr fpe;
+{
+    FSFpePtr    conn = (FSFpePtr) fpe->private;
+    FSBlockDataPtr blockrec,
+                depending;
+    FSClientPtr        *prev, cur;
+    fsFreeACReq        freeac;
+
+    for (prev = &conn->clients; cur = *prev; prev = &cur->next)
+    {
+       if (cur->client == client) {
+           freeac.reqType = FS_FreeAC;
+           freeac.id = cur->acid;
+           freeac.length = sizeof (fsFreeACReq) >> 2;
+           _fs_add_req_log(conn, FS_FreeAC);
+           _fs_write (conn, (char *) &freeac, sizeof (fsFreeACReq));
+           *prev = cur->next;
+           xfree (cur);
+           break;
+       }
+    }
+    /* see if the result is already there */
+    blockrec = (FSBlockDataPtr) conn->blocked_requests;
+    while (blockrec) {
+       if (blockrec->client == client)
+           break;
+       blockrec = blockrec->next;
+    }
+    if (!blockrec)
+       return;
+    if (blockrec->type == FS_LIST_WITH_INFO)
+    {
+       FSBlockedListInfoPtr binfo;
+       binfo = (FSBlockedListInfoPtr) blockrec->data;
+       if (binfo->status == FS_LFWI_REPLY)
+           FD_SET(conn->fs_fd, &_fs_fd_mask);
+       if (binfo->name)
+       {
+           xfree(binfo->name);
+           binfo->name = NULL;
+       }
+       if (binfo->pfi) 
+       {
+           xfree(binfo->pfi->isStringProp);
+           xfree(binfo->pfi->props);
+           xfree(binfo->pfi);
+           binfo->pfi = NULL;
+       }
+    }
+    /* replace the client pointers in this block rec with the chained one */
+    if (depending = blockrec->depending) {
+       blockrec->client = depending->client;
+       blockrec->depending = depending->depending;
+       blockrec = depending;
+    }
+    fs_abort_blockrec(conn, blockrec);
+}
+
+static void
+_fs_client_access (conn, client, sync)
+    FSFpePtr   conn;
+    pointer    client;
+    Bool       sync;
+{
+    FSClientPtr        *prev,      cur;
+    fsCreateACReq          crac;
+    fsSetAuthorizationReq   setac;
+    fsGenericReply         rep;
+    char                   *authorizations;
+    int                            authlen;
+    Bool                   new_cur = FALSE;
+
+    for (prev = &conn->clients; cur = *prev; prev = &cur->next)
+    {
+       if (cur->client == client)
+       {
+           if (prev != &conn->clients)
+           {
+               *prev = cur->next;
+               cur->next = conn->clients;
+               conn->clients = cur;
+           }
+           break;
+       }
+    }
+    if (!cur)
+    {
+       cur = (FSClientPtr) xalloc (sizeof (FSClientRec));
+       if (!cur)
+           return;
+       cur->client = client;
+       cur->next = conn->clients;
+       conn->clients = cur;
+       cur->acid = GetNewFontClientID ();
+       new_cur = TRUE;
+    }
+    if (new_cur || cur->auth_generation != client_auth_generation(client))
+    {
+       if (!new_cur)
+       {
+           fsFreeACReq freeac;
+           freeac.reqType = FS_FreeAC;
+           freeac.id = cur->acid;
+           freeac.length = sizeof (fsFreeACReq) >> 2;
+           _fs_add_req_log(conn, FS_FreeAC);
+           _fs_write (conn, (char *) &freeac, sizeof (fsFreeACReq));
+       }
+       crac.reqType = FS_CreateAC;
+       crac.num_auths = set_font_authorizations(&authorizations, &authlen,
+                                                client);
+       authlen = crac.num_auths ? (authlen + 3) & ~0x3 : 0;
+       crac.length = (sizeof (fsCreateACReq) + authlen) >> 2;
+       crac.acid = cur->acid;
+       _fs_add_req_log(conn, FS_CreateAC);
+       _fs_write(conn, (char *) &crac, sizeof (fsCreateACReq));
+       _fs_write(conn, authorizations, authlen);
+       /* if we're synchronous, open_font will be confused by
+        * the reply; eat it and continue
+        */
+       if (sync)
+       {
+           if (_fs_read(conn, (char *) &rep, sizeof (fsGenericReply)) == -1)
+               return;
+           fs_handle_unexpected(conn, &rep);
+       }
+       /* ignore reply; we don't even care about it */
+       conn->curacid = 0;
+       cur->auth_generation = client_auth_generation(client);
+    }
+    if (conn->curacid != cur->acid)
+    {
+       setac.reqType = FS_SetAuthorization;
+       setac.length = sizeof (fsSetAuthorizationReq) >> 2;
+       setac.id = cur->acid;
+       _fs_add_req_log(conn, FS_SetAuthorization);
+       _fs_write(conn, (char *) &setac, sizeof (fsSetAuthorizationReq));
+       conn->curacid = cur->acid;
+    }
+}
+
+/*
+ * called at server init time
+ */
+
+void
+fs_register_fpe_functions()
+{
+    fs_font_type = RegisterFPEFunctions(fs_name_check,
+                                       fs_init_fpe,
+                                       fs_free_fpe,
+                                       fs_reset_fpe,
+                                       fs_open_font,
+                                       fs_close_font,
+                                       fs_list_fonts,
+                                       fs_start_list_with_info,
+                                       fs_next_list_with_info,
+                                       fs_wakeup,
+                                       fs_client_died,
+                                       _fs_load_glyphs,
+                                       (int (*))0,
+                                       (int (*))0,
+                                       (void (*))0);
+}
+
+static int
+check_fs_open_font(client, fpe, flags, name, namelen, format, fmask, id, ppfont,
+            alias, non_cachable_font)
+    pointer     client;
+    FontPathElementPtr fpe;
+    Mask        flags;
+    char       *name;
+    fsBitmapFormat format;
+    fsBitmapFormatMask fmask;
+    int         namelen;
+    XID         id;
+    FontPtr    *ppfont;
+    char      **alias;
+    FontPtr     non_cachable_font;     /* Not used in this FPE */
+{
+    if (XpClientIsBitmapClient(client))
+       return (fs_open_font(client, fpe, flags, name, namelen, format, 
+                       fmask, id, ppfont, alias, non_cachable_font) );
+    return BadFontName;
+}
+
+static int
+check_fs_list_fonts(client, fpe, pattern, patlen, maxnames, newnames)
+    pointer     client;
+    FontPathElementPtr fpe;
+    char       *pattern;
+    int         patlen;
+    int         maxnames;
+    FontNamesPtr newnames;
+{
+    if (XpClientIsBitmapClient(client))
+       return (fs_list_fonts(client, fpe, pattern, patlen, maxnames, 
+               newnames));
+    return BadFontName;
+}
+
+static int
+check_fs_start_list_with_info(client, fpe, pattern, len, maxnames, pdata)
+    pointer     client;
+    FontPathElementPtr fpe;
+    char       *pattern;
+    int         len;
+    int         maxnames;
+    pointer    *pdata;
+{
+    if (XpClientIsBitmapClient(client))
+       return (fs_start_list_with_info(client, fpe, pattern, len, maxnames,
+               pdata));
+    return BadFontName;
+}
+
+static int
+check_fs_next_list_with_info(client, fpe, namep, namelenp, pFontInfo, numFonts,
+                      private)
+    pointer     client;
+    FontPathElementPtr fpe;
+    char      **namep;
+    int        *namelenp;
+    FontInfoPtr *pFontInfo;
+    int        *numFonts;
+    pointer     private;
+{
+    if (XpClientIsBitmapClient(client))
+       return (fs_next_list_with_info(client, fpe, namep, namelenp, pFontInfo, 
+               numFonts,private));
+    return BadFontName;
+}
+
+void
+check_fs_register_fpe_functions()
+{
+    fs_font_type = RegisterFPEFunctions(fs_name_check,
+                                       fs_init_fpe,
+                                       fs_free_fpe,
+                                       fs_reset_fpe,
+                                       check_fs_open_font,
+                                       fs_close_font,
+                                       check_fs_list_fonts,
+                                       check_fs_start_list_with_info,
+                                       check_fs_next_list_with_info,
+                                       fs_wakeup,
+                                       fs_client_died,
+                                       _fs_load_glyphs,
+                                       (int (*))0,
+                                       (int (*))0,
+                                       (void (*))0);
+}