1/*
2
3Copyright 1990, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25*/
26
27/*
28 * Copyright 1990 Network Computing Devices
29 *
30 * Permission to use, copy, modify, distribute, and sell this software and
31 * its documentation for any purpose is hereby granted without fee, provided
32 * that the above copyright notice appear in all copies and that both that
33 * copyright notice and this permission notice appear in supporting
34 * documentation, and that the names of Network Computing Devices, or Digital
35 * not be used in advertising or publicity pertaining to distribution
36 * of the software without specific, written prior permission.
37 *
38 * NETWORK COMPUTING DEVICES, AND DIGITAL AND DISCLAIM ALL WARRANTIES WITH
39 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
40 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES,
41 * OR DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
42 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
43 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
44 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
45 * THIS SOFTWARE.
46 *
47 * Author:  	Dave Lemke, Network Computing Devices, Inc
48 */
49/*
50 * font server specific font access
51 */
52
53#ifdef HAVE_CONFIG_H
54#include <config.h>
55#endif
56#include "libxfontint.h"
57#include "src/util/replace.h"
58
59#ifdef WIN32
60#define _WILLWINSOCK_
61#endif
62#define FONT_t
63#define TRANS_CLIENT
64#include	"X11/Xtrans/Xtrans.h"
65#include	"X11/Xpoll.h"
66#include	<X11/fonts/FS.h>
67#include	<X11/fonts/FSproto.h>
68#include	<X11/X.h>
69#include	<X11/Xos.h>
70#include	<X11/fonts/fontmisc.h>
71#include	<X11/fonts/fontstruct.h>
72#include	"fservestr.h"
73#include	<X11/fonts/fontutil.h>
74#include	<errno.h>
75#include	<limits.h>
76
77#include	<time.h>
78#define Time_t time_t
79
80#include <stddef.h>
81
82#ifndef MIN
83#define MIN(a,b)    ((a)<(b)?(a):(b))
84#endif
85#define TimeCmp(a,c,b)	((int) ((a) - (b)) c 0)
86
87#define NONZEROMETRICS(pci) ((pci)->leftSideBearing || \
88			     (pci)->rightSideBearing || \
89			     (pci)->ascent || \
90			     (pci)->descent || \
91			     (pci)->characterWidth)
92
93/*
94 * SIZEOF(r) is in bytes, length fields in the protocol are in 32-bit words,
95 * so this converts for doing size comparisons.
96 */
97#define LENGTHOF(r)	(SIZEOF(r) >> 2)
98
99/* Somewhat arbitrary limit on maximum reply size we'll try to read. */
100#define MAX_REPLY_LENGTH	((64 * 1024 * 1024) >> 2)
101
102static int fs_read_glyphs ( FontPathElementPtr fpe, FSBlockDataPtr blockrec );
103static int fs_read_list ( FontPathElementPtr fpe, FSBlockDataPtr blockrec );
104static int fs_read_list_info ( FontPathElementPtr fpe,
105			       FSBlockDataPtr blockrec );
106
107static void fs_block_handler ( void *wt );
108
109static int fs_wakeup ( FontPathElementPtr fpe );
110
111/*
112 * List of all FPEs
113 */
114static FSFpePtr fs_fpes;
115/*
116 * Union of all FPE blockStates
117 */
118static CARD32	fs_blockState;
119
120static int _fs_restart_connection ( FSFpePtr conn );
121static void fs_send_query_bitmaps ( FontPathElementPtr fpe,
122				   FSBlockDataPtr blockrec );
123static int fs_send_close_font (FSFpePtr conn, Font id);
124static void fs_client_died ( pointer client, FontPathElementPtr fpe );
125static void _fs_client_access ( FSFpePtr conn, pointer client, Bool sync );
126static void _fs_client_resolution ( FSFpePtr conn );
127static fsGenericReply *fs_get_reply (FSFpePtr conn, int *error);
128static int fs_await_reply (FSFpePtr conn);
129static void _fs_do_blocked (FSFpePtr conn);
130static void fs_cleanup_bfont (FSFpePtr conn, FSBlockedFontPtr bfont);
131
132char _fs_glyph_undefined;
133char _fs_glyph_requested;
134static char _fs_glyph_zero_length;
135
136static int  generationCount;
137
138static int FontServerRequestTimeout = 30 * 1000;
139
140static void
141_fs_close_server (FSFpePtr conn);
142
143static FSFpePtr
144_fs_init_conn (const char *servername, FontPathElementPtr fpe);
145
146static int
147_fs_wait_connect (FSFpePtr conn);
148
149static int
150_fs_send_init_packets (FSFpePtr conn);
151
152static void
153_fs_check_reconnect (FSFpePtr conn);
154
155static void
156_fs_start_reconnect (FSFpePtr conn);
157
158static void
159_fs_free_conn (FSFpePtr conn);
160
161static int
162fs_free_fpe(FontPathElementPtr fpe);
163
164static void
165fs_fd_handler(int fd, void *data);
166
167/*
168 * Font server access
169 *
170 * the basic idea for the non-blocking access is to have the function
171 * called multiple times until the actual data is returned, instead
172 * of ClientBlocked.
173 *
174 * the first call to the function will cause the request to be sent to
175 * the font server, and a block record to be stored in the fpe's list
176 * of outstanding requests.  the FS block handler also sticks the
177 * proper set of fd's into the select mask.  when data is ready to be
178 * read in, the FS wakeup handler will be hit.  this will read the
179 * data off the wire into the proper block record, and then signal the
180 * client that caused the block so that it can restart.  it will then
181 * call the access function again, which will realize that the data has
182 * arrived and return it.
183 */
184
185
186#ifdef DEBUG
187static void
188_fs_add_req_log(FSFpePtr conn, int opcode)
189{
190    conn->current_seq++;
191    fprintf (stderr, "\t\tRequest: %5d Opcode: %2d\n",
192	     conn->current_seq, opcode);
193    conn->reqbuffer[conn->reqindex].opcode = opcode;
194    conn->reqbuffer[conn->reqindex].sequence = conn->current_seq;
195    conn->reqindex++;
196    if (conn->reqindex == REQUEST_LOG_SIZE)
197	conn->reqindex = 0;
198}
199
200static void
201_fs_add_rep_log (FSFpePtr conn, fsGenericReply *rep)
202{
203    int	    i;
204
205    for (i = 0; i < REQUEST_LOG_SIZE; i++)
206	if (conn->reqbuffer[i].sequence == rep->sequenceNumber)
207	    break;
208    if (i == REQUEST_LOG_SIZE)
209	fprintf (stderr, "\t\t\t\t\tReply:  %5d Opcode: unknown\n",
210		 rep->sequenceNumber);
211    else
212	fprintf (stderr, "\t\t\t\t\tReply:  %5d Opcode: %d\n",
213		 rep->sequenceNumber,
214		 conn->reqbuffer[i].opcode);
215}
216
217#define _fs_reply_failed(rep, name, op) do {                            \
218    if (rep) {                                                          \
219        if (rep->type == FS_Error)                                      \
220            fprintf (stderr, "Error: %d Request: %s\n",                 \
221                     ((fsError *)rep)->request, #name);                 \
222        else                                                            \
223            fprintf (stderr, "Bad Length for %s Reply: %u %s %d\n",     \
224                     #name, (unsigned) rep->length, op, LENGTHOF(name));\
225    }                                                                   \
226} while (0)
227
228#else
229#define _fs_add_req_log(conn,op)    ((conn)->current_seq++)
230#define _fs_add_rep_log(conn,rep)
231#define _fs_reply_failed(rep,name,op)
232#endif
233
234static Bool
235fs_name_check(const char *name)
236{
237    /* Just make sure there is a protocol/ prefix */
238    return (name && *name != '/' && strchr(name, '/'));
239}
240
241static void
242_fs_client_resolution(FSFpePtr conn)
243{
244    fsSetResolutionReq srreq;
245    int         num_res;
246    FontResolutionPtr res;
247
248    res = GetClientResolutions(&num_res);
249
250    if (num_res) {
251	srreq.reqType = FS_SetResolution;
252	srreq.num_resolutions = num_res;
253	srreq.length = (SIZEOF(fsSetResolutionReq) +
254			(num_res * SIZEOF(fsResolution)) + 3) >> 2;
255
256	_fs_add_req_log(conn, FS_SetResolution);
257	if (_fs_write(conn, (char *) &srreq, SIZEOF(fsSetResolutionReq)) != -1)
258	    (void)_fs_write_pad(conn, (char *) res,
259				(num_res * SIZEOF(fsResolution)));
260    }
261}
262
263/*
264 * close font server and remove any state associated with
265 * this connection - this includes any client records.
266 */
267
268static void
269fs_close_conn(FSFpePtr conn)
270{
271    FSClientPtr	client, nclient;
272
273    _fs_close_server (conn);
274
275    for (client = conn->clients; client; client = nclient)
276    {
277	nclient = client->next;
278	free (client);
279    }
280    conn->clients = NULL;
281}
282
283/*
284 * the wakeup handlers have to be set when the FPE is open, and not
285 * removed until it is freed, in order to handle unexpected data, like
286 * events
287 */
288/* ARGSUSED */
289static int
290fs_init_fpe(FontPathElementPtr fpe)
291{
292    FSFpePtr    conn;
293    const char  *name;
294    int         err;
295    int		ret;
296
297    /* open font server */
298    /* create FS specific fpe info */
299    name = fpe->name;
300
301    /* hack for old style names */
302    if (*name == ':')
303	name++;			/* skip ':' */
304
305    conn = _fs_init_conn (name, fpe);
306    if (!conn)
307	err = AllocError;
308    else
309    {
310	err = init_fs_handlers2 (fpe, fs_block_handler);
311	if (err != Successful)
312	{
313	    _fs_free_conn (conn);
314	    err = AllocError;
315	}
316	else
317	{
318	    fpe->private = conn;
319	    conn->next = fs_fpes;
320	    fs_fpes = conn;
321	    ret = _fs_wait_connect (conn);
322	    if (ret != FSIO_READY)
323	    {
324		fs_free_fpe (fpe);
325		err = BadFontPath;
326	    }
327	    else
328		err = Successful;
329	}
330    }
331
332    if (err == Successful)
333    {
334#ifdef DEBUG
335	fprintf (stderr, "connected to FS \"%s\"\n", name);
336#endif
337    }
338    else
339    {
340#ifdef DEBUG
341	fprintf(stderr, "failed to connect to FS \"%s\" %d\n", name, err);
342#endif
343	;
344    }
345    return err;
346}
347
348static int
349fs_reset_fpe(FontPathElementPtr fpe)
350{
351    (void) _fs_send_init_packets((FSFpePtr) fpe->private);
352    return Successful;
353}
354
355/*
356 * this shouldn't be called till all refs to the FPE are gone
357 */
358
359static int
360fs_free_fpe(FontPathElementPtr fpe)
361{
362    FSFpePtr    conn = (FSFpePtr) fpe->private, *prev;
363
364    /* unhook from chain of all font servers */
365    for (prev = &fs_fpes; *prev; prev = &(*prev)->next)
366    {
367	if (*prev == conn)
368	{
369	    *prev = conn->next;
370	    break;
371	}
372    }
373    _fs_unmark_block (conn, conn->blockState);
374    fs_close_conn(conn);
375    remove_fs_handlers2(fpe, fs_block_handler, fs_fpes == 0);
376    _fs_free_conn (conn);
377    fpe->private = (pointer) 0;
378
379#ifdef DEBUG
380    fprintf (stderr, "disconnect from FS \"%s\"\n", fpe->name);
381#endif
382
383    return Successful;
384}
385
386static      FSBlockDataPtr
387fs_new_block_rec(FontPathElementPtr fpe, pointer client, int type)
388{
389    FSBlockDataPtr blockrec,
390                *prev;
391    FSFpePtr    conn = (FSFpePtr) fpe->private;
392    int         size;
393
394    switch (type) {
395    case FS_OPEN_FONT:
396	size = sizeof(FSBlockedFontRec);
397	break;
398    case FS_LOAD_GLYPHS:
399	size = sizeof(FSBlockedGlyphRec);
400	break;
401    case FS_LIST_FONTS:
402	size = sizeof(FSBlockedListRec);
403	break;
404    case FS_LIST_WITH_INFO:
405	size = sizeof(FSBlockedListInfoRec);
406	break;
407    default:
408	size = 0;
409	break;
410    }
411    blockrec = malloc(sizeof(FSBlockDataRec) + size);
412    if (!blockrec)
413	return (FSBlockDataPtr) 0;
414    blockrec->data = (pointer) (blockrec + 1);
415    blockrec->client = client;
416    blockrec->sequenceNumber = -1;
417    blockrec->errcode = StillWorking;
418    blockrec->type = type;
419    blockrec->depending = 0;
420    blockrec->next = (FSBlockDataPtr) 0;
421
422    /* stick it on the end of the list (since its expected last) */
423    for (prev = &conn->blockedRequests; *prev; prev = &(*prev)->next)
424	;
425    *prev = blockrec;
426
427    return blockrec;
428}
429
430static void
431_fs_set_pending_reply (FSFpePtr conn)
432{
433    FSBlockDataPtr  blockrec;
434
435    for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
436	if (blockrec->errcode == StillWorking)
437	    break;
438    if (blockrec)
439    {
440	conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
441	_fs_mark_block (conn, FS_PENDING_REPLY);
442    }
443    else
444	_fs_unmark_block (conn, FS_PENDING_REPLY);
445}
446
447static void
448_fs_remove_block_rec(FSFpePtr conn, FSBlockDataPtr blockrec)
449{
450    FSBlockDataPtr *prev;
451
452    for (prev = &conn->blockedRequests; *prev; prev = &(*prev)->next)
453	if (*prev == blockrec)
454	{
455	    *prev = blockrec->next;
456	    break;
457	}
458    if (blockrec->type == FS_LOAD_GLYPHS)
459    {
460	FSBlockedGlyphPtr bglyph = (FSBlockedGlyphPtr)blockrec->data;
461	if (bglyph->num_expected_ranges)
462	    free(bglyph->expected_ranges);
463    }
464    free(blockrec);
465    _fs_set_pending_reply (conn);
466}
467
468static void
469_fs_signal_clients_depending(FSClientsDependingPtr *clients_depending)
470{
471    FSClientsDependingPtr p;
472
473    while ((p = *clients_depending))
474    {
475	*clients_depending = p->next;
476	ClientSignal(p->client);
477	free(p);
478    }
479}
480
481static int
482_fs_add_clients_depending(FSClientsDependingPtr *clients_depending, pointer client)
483{
484    FSClientsDependingPtr   new, cd;
485
486    for (; (cd = *clients_depending);
487	 clients_depending = &(*clients_depending)->next)
488    {
489	if (cd->client == client)
490	    return Suspended;
491    }
492
493    new = malloc (sizeof (FSClientsDependingRec));
494    if (!new)
495	return BadAlloc;
496
497    new->client = client;
498    new->next = 0;
499    *clients_depending = new;
500    return Suspended;
501}
502
503static void
504conn_start_listening(FSFpePtr conn)
505{
506    if (!conn->fs_listening) {
507	add_fs_fd(conn->fs_fd, fs_fd_handler, conn->fpe);
508	conn->fs_listening = TRUE;
509    }
510}
511
512static void
513conn_stop_listening(FSFpePtr conn)
514{
515    if (conn->fs_listening) {
516	remove_fs_fd(conn->fs_fd);
517	conn->fs_listening = FALSE;
518    }
519}
520
521/*
522 * When a request is aborted due to a font server failure,
523 * signal any depending clients to restart their dependent
524 * requests
525 */
526static void
527_fs_clean_aborted_blockrec(FSFpePtr conn, FSBlockDataPtr blockrec)
528{
529    switch(blockrec->type) {
530    case FS_OPEN_FONT: {
531	FSBlockedFontPtr bfont = (FSBlockedFontPtr)blockrec->data;
532
533	fs_cleanup_bfont (conn, bfont);
534	_fs_signal_clients_depending(&bfont->clients_depending);
535	break;
536    }
537    case FS_LOAD_GLYPHS: {
538	FSBlockedGlyphPtr bglyph = (FSBlockedGlyphPtr)blockrec->data;
539
540	_fs_clean_aborted_loadglyphs(bglyph->pfont,
541				     bglyph->num_expected_ranges,
542				     bglyph->expected_ranges);
543	_fs_signal_clients_depending(&bglyph->clients_depending);
544	break;
545    }
546    case FS_LIST_FONTS:
547	break;
548    case FS_LIST_WITH_INFO: {
549	FSBlockedListInfoPtr binfo;
550	binfo = (FSBlockedListInfoPtr) blockrec->data;
551	if (binfo->status == FS_LFWI_REPLY)
552	    conn_start_listening(conn);
553	_fs_free_props (&binfo->info);
554    }
555    default:
556	break;
557    }
558}
559
560static void
561fs_abort_blockrec(FSFpePtr conn, FSBlockDataPtr blockrec)
562{
563    _fs_clean_aborted_blockrec (conn, blockrec);
564    _fs_remove_block_rec (conn, blockrec);
565}
566
567/*
568 * Tell the font server we've failed to complete an open and
569 * then unload the partially created font
570 */
571static void
572fs_cleanup_bfont (FSFpePtr conn, FSBlockedFontPtr bfont)
573{
574    if (bfont->pfont)
575    {
576	/* make sure the FS knows we choked on it */
577	fs_send_close_font(conn, bfont->fontid);
578
579	/*
580	 * Either unload the font if it's being opened for
581	 * the first time, or smash the generation field to
582	 * mark this font as an orphan
583	 */
584	if (!(bfont->flags & FontReopen))
585	{
586	    if (bfont->freeFont)
587		(*bfont->pfont->unload_font) (bfont->pfont);
588#ifdef DEBUG
589	    else
590		fprintf (stderr, "Not freeing other font in cleanup_bfont\n");
591#endif
592	    bfont->pfont = 0;
593	}
594	else
595	{
596	    FSFontDataRec *fsd = (FSFontDataRec *)bfont->pfont->fpePrivate;
597	    fsd->generation = -1;
598	}
599    }
600}
601
602/*
603 * Check to see if a complete reply is waiting
604 */
605static fsGenericReply *
606fs_get_reply (FSFpePtr conn, int *error)
607{
608    char	    *buf;
609    fsGenericReply  *rep;
610    int		    ret;
611
612    /* block if the connection is down or paused in lfwi */
613    if (conn->fs_fd == -1 || !conn->fs_listening)
614    {
615	*error = FSIO_BLOCK;
616	return 0;
617    }
618
619    ret = _fs_start_read (conn, sizeof (fsGenericReply), &buf);
620    if (ret != FSIO_READY)
621    {
622	*error = FSIO_BLOCK;
623	return 0;
624    }
625
626    rep = (fsGenericReply *) buf;
627
628    /*
629     * Refuse to accept replies longer than a maximum reasonable length,
630     * before we pass to _fs_start_read, since it will try to resize the
631     * incoming connection buffer to this size.  Also avoids integer overflow
632     * on 32-bit systems.
633     */
634    if (rep->length > MAX_REPLY_LENGTH)
635    {
636	ErrorF("fserve: reply length %ld > MAX_REPLY_LENGTH, disconnecting"
637	       " from font server\n", (long)rep->length);
638	_fs_connection_died (conn);
639	*error = FSIO_ERROR;
640	return 0;
641    }
642
643    ret = _fs_start_read (conn, rep->length << 2, &buf);
644    if (ret != FSIO_READY)
645    {
646	*error = FSIO_BLOCK;
647	return 0;
648    }
649
650    *error = FSIO_READY;
651
652    return (fsGenericReply *) buf;
653}
654
655static Bool
656fs_reply_ready (FSFpePtr conn)
657{
658    fsGenericReply  *rep;
659
660    if (conn->fs_fd == -1 || !conn->fs_listening)
661	return FALSE;
662    if (fs_data_read (conn) < sizeof (fsGenericReply))
663	return FALSE;
664    rep = (fsGenericReply *) (conn->inBuf.buf + conn->inBuf.remove);
665    if (fs_data_read (conn) < rep->length << 2)
666	return FALSE;
667    return TRUE;
668}
669
670static void
671_fs_pending_reply (FSFpePtr conn)
672{
673    if (!(conn->blockState & FS_PENDING_REPLY))
674    {
675	_fs_mark_block (conn, FS_PENDING_REPLY);
676	conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
677    }
678}
679
680static void
681_fs_prepare_for_reply (FSFpePtr conn)
682{
683    _fs_pending_reply (conn);
684    _fs_flush (conn);
685}
686
687/*
688 * Block (for a while) awaiting a complete reply
689 */
690static int
691fs_await_reply (FSFpePtr conn)
692{
693    int		    ret;
694
695    if (conn->blockState & FS_COMPLETE_REPLY)
696	return FSIO_READY;
697
698    while (!fs_get_reply (conn, &ret))
699    {
700	if (ret != FSIO_BLOCK)
701	    return ret;
702	if (_fs_wait_for_readable (conn, FontServerRequestTimeout) != FSIO_READY)
703	{
704	    _fs_connection_died (conn);
705	    return FSIO_ERROR;
706	}
707    }
708    return FSIO_READY;
709}
710
711/*
712 * Process the reply to an OpenBitmapFont request
713 */
714static int
715fs_read_open_font(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
716{
717    FSFpePtr		    conn = (FSFpePtr) fpe->private;
718    FSBlockedFontPtr	    bfont = (FSBlockedFontPtr) blockrec->data;
719    fsOpenBitmapFontReply   *rep;
720    FSBlockDataPtr	    blockOrig;
721    FSBlockedFontPtr	    origBfont;
722    int			    ret;
723
724    rep = (fsOpenBitmapFontReply *) fs_get_reply (conn, &ret);
725    if (!rep || rep->type == FS_Error ||
726	(rep->length != LENGTHOF(fsOpenBitmapFontReply)))
727    {
728	if (ret == FSIO_BLOCK)
729	    return StillWorking;
730	if (rep)
731	    _fs_done_read (conn, rep->length << 2);
732	fs_cleanup_bfont (conn, bfont);
733	_fs_reply_failed (rep, fsOpenBitmapFontReply, "!=");
734	return BadFontName;
735    }
736
737    /* If we're not reopening a font and FS detected a duplicate font
738       open request, replace our reference to the new font with a
739       reference to an existing font (possibly one not finished
740       opening).  If this is a reopen, keep the new font reference...
741       it's got the metrics and extents we read when the font was opened
742       before.  This also gives us the freedom to easily close the font
743       if we we decide (in fs_read_query_info()) that we don't like what
744       we got. */
745
746    if (rep->otherid && !(bfont->flags & FontReopen))
747    {
748	fs_cleanup_bfont (conn, bfont);
749
750	/* Find old font if we're completely done getting it from server. */
751	bfont->pfont = find_old_font(rep->otherid);
752	bfont->freeFont = FALSE;
753	bfont->fontid = rep->otherid;
754	bfont->state = FS_DONE_REPLY;
755	/*
756	 * look for a blocked request to open the same font
757	 */
758	for (blockOrig = conn->blockedRequests;
759		blockOrig;
760		blockOrig = blockOrig->next)
761	{
762	    if (blockOrig != blockrec && blockOrig->type == FS_OPEN_FONT)
763	    {
764		origBfont = (FSBlockedFontPtr) blockOrig->data;
765		if (origBfont->fontid == rep->otherid)
766		{
767		    blockrec->depending = blockOrig->depending;
768		    blockOrig->depending = blockrec;
769		    bfont->state = FS_DEPENDING;
770		    bfont->pfont = origBfont->pfont;
771		    break;
772		}
773	    }
774	}
775	if (bfont->pfont == NULL)
776	{
777	    /* XXX - something nasty happened */
778	    ret = BadFontName;
779	}
780	else
781	    ret = AccessDone;
782    }
783    else
784    {
785	bfont->pfont->info.cachable = rep->cachable != 0;
786	bfont->state = FS_INFO_REPLY;
787	/*
788	 * Reset the blockrec for the next reply
789	 */
790	blockrec->sequenceNumber = bfont->queryInfoSequence;
791	conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
792	ret = StillWorking;
793    }
794    _fs_done_read (conn, rep->length << 2);
795    return ret;
796}
797
798static Bool
799fs_fonts_match (FontInfoPtr pInfo1, FontInfoPtr pInfo2)
800{
801    int	    i;
802
803    if (pInfo1->firstCol != pInfo2->firstCol ||
804	pInfo1->lastCol != pInfo2->lastCol ||
805	pInfo1->firstRow != pInfo2->firstRow ||
806	pInfo1->lastRow != pInfo2->lastRow ||
807	pInfo1->defaultCh != pInfo2->defaultCh ||
808	pInfo1->noOverlap != pInfo2->noOverlap ||
809	pInfo1->terminalFont != pInfo2->terminalFont ||
810	pInfo1->constantMetrics != pInfo2->constantMetrics ||
811	pInfo1->constantWidth != pInfo2->constantWidth ||
812	pInfo1->inkInside != pInfo2->inkInside ||
813	pInfo1->inkMetrics != pInfo2->inkMetrics ||
814	pInfo1->allExist != pInfo2->allExist ||
815	pInfo1->drawDirection != pInfo2->drawDirection ||
816	pInfo1->cachable != pInfo2->cachable ||
817	pInfo1->anamorphic != pInfo2->anamorphic ||
818	pInfo1->maxOverlap != pInfo2->maxOverlap ||
819	pInfo1->fontAscent != pInfo2->fontAscent ||
820	pInfo1->fontDescent != pInfo2->fontDescent ||
821	pInfo1->nprops != pInfo2->nprops)
822	return FALSE;
823
824#define MATCH(xci1, xci2) \
825    (((xci1).leftSideBearing == (xci2).leftSideBearing) && \
826     ((xci1).rightSideBearing == (xci2).rightSideBearing) && \
827     ((xci1).characterWidth == (xci2).characterWidth) && \
828     ((xci1).ascent == (xci2).ascent) && \
829     ((xci1).descent == (xci2).descent) && \
830     ((xci1).attributes == (xci2).attributes))
831
832    if (!MATCH(pInfo1->maxbounds, pInfo2->maxbounds) ||
833	!MATCH(pInfo1->minbounds, pInfo2->minbounds) ||
834	!MATCH(pInfo1->ink_maxbounds, pInfo2->ink_maxbounds) ||
835	!MATCH(pInfo1->ink_minbounds, pInfo2->ink_minbounds))
836	return FALSE;
837
838#undef MATCH
839
840    for (i = 0; i < pInfo1->nprops; i++)
841	if (pInfo1->isStringProp[i] !=
842		pInfo2->isStringProp[i] ||
843	    pInfo1->props[i].name !=
844		pInfo2->props[i].name ||
845	    pInfo1->props[i].value !=
846		pInfo2->props[i].value)
847	{
848	    return FALSE;
849	}
850    return TRUE;
851}
852
853static int
854fs_read_query_info(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
855{
856    FSBlockedFontPtr	bfont = (FSBlockedFontPtr) blockrec->data;
857    FSFpePtr		conn = (FSFpePtr) fpe->private;
858    fsQueryXInfoReply	*rep;
859    char		*buf;
860    long		bufleft; /* length of reply left to use */
861    fsPropInfo		*pi;
862    fsPropOffset	*po;
863    pointer		pd;
864    FontInfoPtr		pInfo;
865    FontInfoRec		tempInfo;
866    int			err;
867    int			ret;
868
869    rep = (fsQueryXInfoReply *) fs_get_reply (conn, &ret);
870    if (!rep || rep->type == FS_Error ||
871	(rep->length < LENGTHOF(fsQueryXInfoReply)))
872    {
873	if (ret == FSIO_BLOCK)
874	    return StillWorking;
875	if (rep)
876	    _fs_done_read (conn, rep->length << 2);
877	fs_cleanup_bfont (conn, bfont);
878	_fs_reply_failed (rep, fsQueryXInfoReply, "<");
879	return BadFontName;
880    }
881
882    /* If this is a reopen, accumulate the query info into a dummy
883       font and compare to our original data. */
884    if (bfont->flags & FontReopen)
885	pInfo = &tempInfo;
886    else
887	pInfo = &bfont->pfont->info;
888
889    buf = (char *) rep;
890    buf += SIZEOF(fsQueryXInfoReply);
891
892    bufleft = rep->length << 2;
893    bufleft -= SIZEOF(fsQueryXInfoReply);
894
895    /* move the data over */
896    fsUnpack_XFontInfoHeader(rep, pInfo);
897
898    /* compute accelerators */
899    _fs_init_fontinfo(conn, pInfo);
900
901    /* Compute offsets into the reply */
902    if (bufleft < SIZEOF(fsPropInfo))
903    {
904	ret = -1;
905#ifdef DEBUG
906	fprintf(stderr, "fsQueryXInfo: bufleft (%ld) < SIZEOF(fsPropInfo)\n",
907		bufleft);
908#endif
909	goto bail;
910    }
911    pi = (fsPropInfo *) buf;
912    buf += SIZEOF (fsPropInfo);
913    bufleft -= SIZEOF(fsPropInfo);
914
915    if ((bufleft / SIZEOF(fsPropOffset)) < pi->num_offsets)
916    {
917	ret = -1;
918#ifdef DEBUG
919	fprintf(stderr,
920		"fsQueryXInfo: bufleft (%ld) / SIZEOF(fsPropOffset) < %u\n",
921		bufleft, (unsigned) pi->num_offsets);
922#endif
923	goto bail;
924    }
925    po = (fsPropOffset *) buf;
926    buf += pi->num_offsets * SIZEOF(fsPropOffset);
927    bufleft -= pi->num_offsets * SIZEOF(fsPropOffset);
928
929    if (bufleft < pi->data_len)
930    {
931	ret = -1;
932#ifdef DEBUG
933	fprintf(stderr,
934		"fsQueryXInfo: bufleft (%ld) < data_len (%u)\n",
935		bufleft, (unsigned) pi->data_len);
936#endif
937	goto bail;
938    }
939    pd = (pointer) buf;
940    buf += pi->data_len;
941    bufleft -= pi->data_len;
942
943    /* convert the properties and step over the reply */
944    ret = _fs_convert_props(pi, po, pd, pInfo);
945  bail:
946    _fs_done_read (conn, rep->length << 2);
947
948    if (ret == -1)
949    {
950	fs_cleanup_bfont (conn, bfont);
951	return AllocError;
952    }
953
954    if (bfont->flags & FontReopen)
955    {
956	/* We're reopening a font that we lost because of a downed
957	   connection.  In the interest of avoiding corruption from
958	   opening a different font than the old one (we already have
959	   its metrics, extents, and probably some of its glyphs),
960	   verify that the metrics and properties all match.  */
961
962	if (fs_fonts_match (pInfo, &bfont->pfont->info))
963	{
964	    err = Successful;
965	    bfont->state = FS_DONE_REPLY;
966	}
967	else
968	{
969	    fs_cleanup_bfont (conn, bfont);
970	    err = BadFontName;
971	}
972	_fs_free_props (pInfo);
973
974	return err;
975    }
976
977    /*
978     * Ask for terminal format fonts if possible
979     */
980    if (bfont->pfont->info.terminalFont)
981	bfont->format = ((bfont->format & ~ (BitmapFormatImageRectMask)) |
982			 BitmapFormatImageRectMax);
983
984    /*
985     * Figure out if the whole font should get loaded right now.
986     */
987    if (glyphCachingMode == CACHING_OFF ||
988	(glyphCachingMode == CACHE_16_BIT_GLYPHS
989	 && !bfont->pfont->info.lastRow))
990    {
991	bfont->flags |= FontLoadAll;
992    }
993
994    /*
995     * Ready to send the query bitmaps; the terminal font bit has
996     * been computed and glyphCaching has been considered
997     */
998    if (bfont->flags & FontLoadBitmaps)
999    {
1000	fs_send_query_bitmaps (fpe, blockrec);
1001	_fs_flush (conn);
1002    }
1003
1004    bfont->state = FS_EXTENT_REPLY;
1005
1006    /*
1007     * Reset the blockrec for the next reply
1008     */
1009    blockrec->sequenceNumber = bfont->queryExtentsSequence;
1010    conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
1011
1012    return StillWorking;
1013}
1014
1015static int
1016fs_read_extent_info(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
1017{
1018    FSFpePtr		    conn = (FSFpePtr) fpe->private;
1019    FSBlockedFontPtr	    bfont = (FSBlockedFontPtr) blockrec->data;
1020    FSFontDataPtr	    fsd = (FSFontDataPtr) bfont->pfont->fpePrivate;
1021    FSFontPtr		    fsfont = (FSFontPtr) bfont->pfont->fontPrivate;
1022    fsQueryXExtents16Reply  *rep;
1023    char		    *buf;
1024    int			    i;
1025    int			    numExtents;
1026    int			    numInfos;
1027    int			    ret;
1028    Bool		    haveInk = FALSE; /* need separate ink metrics? */
1029    CharInfoPtr		    ci, pCI;
1030    char		    *fsci;
1031    fsXCharInfo		    fscilocal;
1032    FontInfoRec		    *fi = &bfont->pfont->info;
1033
1034    rep = (fsQueryXExtents16Reply *) fs_get_reply (conn, &ret);
1035    if (!rep || rep->type == FS_Error ||
1036	(rep->length < LENGTHOF(fsQueryXExtents16Reply)))
1037    {
1038	if (ret == FSIO_BLOCK)
1039	    return StillWorking;
1040	if (rep)
1041	    _fs_done_read (conn, rep->length << 2);
1042	fs_cleanup_bfont (conn, bfont);
1043	_fs_reply_failed (rep, fsQueryXExtents16Reply, "<");
1044	return BadFontName;
1045    }
1046
1047    /* move the data over */
1048    /* need separate inkMetrics for fixed font server protocol version */
1049    numExtents = rep->num_extents;
1050    numInfos = numExtents;
1051    if (bfont->pfont->info.terminalFont && conn->fsMajorVersion > 1)
1052    {
1053	numInfos *= 2;
1054	haveInk = TRUE;
1055    }
1056    if (numInfos >= (INT_MAX / sizeof(CharInfoRec))) {
1057#ifdef DEBUG
1058	fprintf(stderr,
1059		"fsQueryXExtents16: numInfos (%d) >= %ld\n",
1060		numInfos, (INT_MAX / sizeof(CharInfoRec)));
1061#endif
1062	pCI = NULL;
1063    }
1064    else if (numExtents > ((rep->length - LENGTHOF(fsQueryXExtents16Reply))
1065			    / LENGTHOF(fsXCharInfo))) {
1066#ifdef DEBUG
1067	fprintf(stderr,
1068		"fsQueryXExtents16: numExtents (%d) > (%u - %d) / %d\n",
1069		numExtents, (unsigned) rep->length,
1070		LENGTHOF(fsQueryXExtents16Reply), LENGTHOF(fsXCharInfo));
1071#endif
1072	pCI = NULL;
1073    }
1074    else
1075	pCI = mallocarray(numInfos, sizeof(CharInfoRec));
1076
1077    if (!pCI)
1078    {
1079	_fs_done_read (conn, rep->length << 2);
1080	fs_cleanup_bfont(conn, bfont);
1081	return AllocError;
1082    }
1083    fsfont->encoding = pCI;
1084    if (haveInk)
1085	fsfont->inkMetrics = pCI + numExtents;
1086    else
1087        fsfont->inkMetrics = pCI;
1088
1089    buf = (char *) rep;
1090    buf += SIZEOF (fsQueryXExtents16Reply);
1091    fsci = buf;
1092
1093    fsd->glyphs_to_get = 0;
1094    ci = fsfont->inkMetrics;
1095    for (i = 0; i < numExtents; i++)
1096    {
1097	memcpy(&fscilocal, fsci, SIZEOF(fsXCharInfo)); /* align it */
1098	_fs_convert_char_info(&fscilocal, &ci->metrics);
1099	/* Bounds check. */
1100	if (ci->metrics.ascent > fi->maxbounds.ascent)
1101	{
1102	    ErrorF("fserve: warning: %s %s ascent (%d) > maxascent (%d)\n",
1103		   fpe->name, fsd->name,
1104		   ci->metrics.ascent, fi->maxbounds.ascent);
1105	    ci->metrics.ascent = fi->maxbounds.ascent;
1106	}
1107	if (ci->metrics.descent > fi->maxbounds.descent)
1108	{
1109	    ErrorF("fserve: warning: %s %s descent (%d) > maxdescent (%d)\n",
1110		   fpe->name, fsd->name,
1111		   ci->metrics.descent, fi->maxbounds.descent);
1112	    ci->metrics.descent = fi->maxbounds.descent;
1113	}
1114	fsci = fsci + SIZEOF(fsXCharInfo);
1115	/* Initialize the bits field for later glyph-caching use */
1116	if (NONZEROMETRICS(&ci->metrics))
1117	{
1118	    if (!haveInk &&
1119		(ci->metrics.leftSideBearing == ci->metrics.rightSideBearing ||
1120		 ci->metrics.ascent == -ci->metrics.descent))
1121		pCI[i].bits = &_fs_glyph_zero_length;
1122	    else
1123	    {
1124		pCI[i].bits = &_fs_glyph_undefined;
1125		fsd->glyphs_to_get++;
1126	    }
1127	}
1128	else
1129	    pCI[i].bits = (char *)0;
1130	ci++;
1131    }
1132
1133    /* Done with reply */
1134    _fs_done_read (conn, rep->length << 2);
1135
1136    /* build bitmap metrics, ImageRectMax style */
1137    if (haveInk)
1138    {
1139	CharInfoPtr ii;
1140
1141	ci = fsfont->encoding;
1142	ii = fsfont->inkMetrics;
1143	for (i = 0; i < numExtents; i++, ci++, ii++)
1144	{
1145	    if (NONZEROMETRICS(&ii->metrics))
1146	    {
1147		ci->metrics.leftSideBearing = FONT_MIN_LEFT(fi);
1148		ci->metrics.rightSideBearing = FONT_MAX_RIGHT(fi);
1149		ci->metrics.ascent = FONT_MAX_ASCENT(fi);
1150		ci->metrics.descent = FONT_MAX_DESCENT(fi);
1151		ci->metrics.characterWidth = FONT_MAX_WIDTH(fi);
1152		ci->metrics.attributes = ii->metrics.attributes;
1153	    }
1154	    else
1155	    {
1156		ci->metrics = ii->metrics;
1157	    }
1158	    /* Bounds check. */
1159	    if (ci->metrics.ascent > fi->maxbounds.ascent)
1160	    {
1161		ErrorF("fserve: warning: %s %s ascent (%d) "
1162		       "> maxascent (%d)\n",
1163		       fpe->name, fsd->name,
1164		       ci->metrics.ascent, fi->maxbounds.ascent);
1165		ci->metrics.ascent = fi->maxbounds.ascent;
1166	    }
1167	    if (ci->metrics.descent > fi->maxbounds.descent)
1168	    {
1169		ErrorF("fserve: warning: %s %s descent (%d) "
1170		       "> maxdescent (%d)\n",
1171		       fpe->name, fsd->name,
1172		       ci->metrics.descent, fi->maxbounds.descent);
1173		ci->metrics.descent = fi->maxbounds.descent;
1174	    }
1175	}
1176    }
1177    {
1178	unsigned int r, c, numCols, firstCol;
1179
1180	firstCol = bfont->pfont->info.firstCol;
1181	numCols = bfont->pfont->info.lastCol - firstCol + 1;
1182	c = bfont->pfont->info.defaultCh;
1183	fsfont->pDefault = 0;
1184	if (bfont->pfont->info.lastRow)
1185	{
1186	    r = c >> 8;
1187	    r -= bfont->pfont->info.firstRow;
1188	    c &= 0xff;
1189	    c -= firstCol;
1190	    if (r < bfont->pfont->info.lastRow-bfont->pfont->info.firstRow+1 &&
1191		c < numCols)
1192		fsfont->pDefault = &pCI[r * numCols + c];
1193	}
1194	else
1195	{
1196	    c -= firstCol;
1197	    if (c < numCols)
1198		fsfont->pDefault = &pCI[c];
1199	}
1200    }
1201    bfont->state = FS_GLYPHS_REPLY;
1202
1203    if (bfont->flags & FontLoadBitmaps)
1204    {
1205	/*
1206	 * Reset the blockrec for the next reply
1207	 */
1208	blockrec->sequenceNumber = bfont->queryBitmapsSequence;
1209	conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
1210	return StillWorking;
1211    }
1212    return Successful;
1213}
1214
1215#ifdef DEBUG
1216static const char *fs_open_states[] = {
1217    "OPEN_REPLY  ",
1218    "INFO_REPLY  ",
1219    "EXTENT_REPLY",
1220    "GLYPHS_REPLY",
1221    "DONE_REPLY  ",
1222    "DEPENDING   ",
1223};
1224#endif
1225
1226static int
1227fs_do_open_font(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
1228{
1229    FSBlockedFontPtr	bfont = (FSBlockedFontPtr) blockrec->data;
1230    int			err;
1231
1232#ifdef DEBUG
1233    fprintf (stderr, "fs_do_open_font state %s %s\n",
1234	     fs_open_states[bfont->state],
1235	     ((FSFontDataPtr) (bfont->pfont->fpePrivate))->name);
1236#endif
1237    err = BadFontName;
1238    switch (bfont->state) {
1239    case FS_OPEN_REPLY:
1240	err = fs_read_open_font(fpe, blockrec);
1241	if (err != StillWorking) {	/* already loaded, or error */
1242	    /* if font's already loaded, massage error code */
1243	    switch (bfont->state) {
1244	    case FS_DONE_REPLY:
1245		err = Successful;
1246		break;
1247	    case FS_DEPENDING:
1248		err = StillWorking;
1249		break;
1250	    }
1251	}
1252	break;
1253    case FS_INFO_REPLY:
1254	err = fs_read_query_info(fpe, blockrec);
1255	break;
1256    case FS_EXTENT_REPLY:
1257	err = fs_read_extent_info(fpe, blockrec);
1258	break;
1259    case FS_GLYPHS_REPLY:
1260	if (bfont->flags & FontLoadBitmaps)
1261	    err = fs_read_glyphs(fpe, blockrec);
1262	break;
1263    case FS_DEPENDING:		/* can't happen */
1264    default:
1265	break;
1266    }
1267#ifdef DEBUG
1268    fprintf (stderr, "fs_do_open_font err %d\n", err);
1269#endif
1270    if (err != StillWorking)
1271    {
1272	bfont->state = FS_DONE_REPLY;	/* for _fs_load_glyphs() */
1273	while ((blockrec = blockrec->depending))
1274	{
1275	    bfont = (FSBlockedFontPtr) blockrec->data;
1276	    bfont->state = FS_DONE_REPLY;	/* for _fs_load_glyphs() */
1277	}
1278    }
1279    return err;
1280}
1281
1282void
1283_fs_mark_block (FSFpePtr conn, CARD32 mask)
1284{
1285    conn->blockState |= mask;
1286    fs_blockState |= mask;
1287}
1288
1289void
1290_fs_unmark_block (FSFpePtr conn, CARD32 mask)
1291{
1292    FSFpePtr	c;
1293
1294    if (conn->blockState & mask)
1295    {
1296	conn->blockState &= ~mask;
1297	fs_blockState = 0;
1298	for (c = fs_fpes; c; c = c->next)
1299	    fs_blockState |= c->blockState;
1300    }
1301}
1302
1303/* ARGSUSED */
1304static void
1305fs_block_handler(void *wt)
1306{
1307    CARD32	now, earliest, wakeup;
1308    int		soonest;
1309    FSFpePtr    conn;
1310
1311    /*
1312     * Flush all pending output
1313     */
1314    if (fs_blockState & FS_PENDING_WRITE)
1315	for (conn = fs_fpes; conn; conn = conn->next)
1316	    if (conn->blockState & FS_PENDING_WRITE)
1317		_fs_flush (conn);
1318    /*
1319     * Check for any fpe with a complete reply, set sleep time to zero
1320     */
1321    if (fs_blockState & FS_COMPLETE_REPLY)
1322	adjust_fs_wait_for_delay(wt, 0);
1323    /*
1324     * Walk through fpe list computing sleep time
1325     */
1326    else if (fs_blockState & (FS_BROKEN_WRITE|
1327			      FS_BROKEN_CONNECTION|
1328			      FS_PENDING_REPLY|
1329			      FS_RECONNECTING))
1330    {
1331	now = GetTimeInMillis ();
1332	earliest = now + 10000000;
1333	for (conn = fs_fpes; conn; conn = conn->next)
1334	{
1335	    if (conn->blockState & FS_RECONNECTING)
1336	    {
1337		wakeup = conn->blockedConnectTime;
1338		if (TimeCmp (wakeup, <, earliest))
1339		    earliest = wakeup;
1340	    }
1341	    if (conn->blockState & FS_BROKEN_CONNECTION)
1342	    {
1343		wakeup = conn->brokenConnectionTime;
1344		if (TimeCmp (wakeup, <, earliest))
1345		    earliest = wakeup;
1346	    }
1347	    if (conn->blockState & FS_BROKEN_WRITE)
1348	    {
1349		wakeup = conn->brokenWriteTime;
1350		if (TimeCmp (wakeup, <, earliest))
1351		    earliest = wakeup;
1352	    }
1353	    if (conn->blockState & FS_PENDING_REPLY)
1354	    {
1355		wakeup = conn->blockedReplyTime;
1356		if (TimeCmp (wakeup, <, earliest))
1357		    earliest = wakeup;
1358	    }
1359	}
1360	soonest = earliest - now;
1361	if (soonest < 0)
1362	    soonest = 0;
1363	adjust_fs_wait_for_delay(wt, soonest);
1364    }
1365}
1366
1367static void
1368fs_handle_unexpected(FSFpePtr conn, fsGenericReply *rep)
1369{
1370    if (rep->type == FS_Event && rep->data1 == KeepAlive)
1371    {
1372	fsNoopReq   req;
1373
1374	/* ping it back */
1375	req.reqType = FS_Noop;
1376	req.length = SIZEOF(fsNoopReq) >> 2;
1377	_fs_add_req_log(conn, FS_Noop);
1378	_fs_write(conn, (char *) &req, SIZEOF(fsNoopReq));
1379    }
1380    /* this should suck up unexpected replies and events */
1381    _fs_done_read (conn, rep->length << 2);
1382}
1383
1384static void
1385fs_read_reply (FontPathElementPtr fpe, pointer client)
1386{
1387    FSFpePtr	    conn = (FSFpePtr) fpe->private;
1388    FSBlockDataPtr  blockrec;
1389    int		    ret;
1390    int		    err;
1391    fsGenericReply  *rep;
1392
1393    if ((rep = fs_get_reply (conn, &ret)))
1394    {
1395	_fs_add_rep_log (conn, rep);
1396	for (blockrec = conn->blockedRequests;
1397	     blockrec;
1398	     blockrec = blockrec->next)
1399	{
1400	    if (blockrec->sequenceNumber == rep->sequenceNumber)
1401		break;
1402	}
1403	err = Successful;
1404	if (!blockrec)
1405	{
1406	    fs_handle_unexpected(conn, rep);
1407	}
1408	else
1409	{
1410	    /*
1411	     * go read it, and if we're done,
1412	     * wake up the appropriate client
1413	     */
1414	    switch (blockrec->type) {
1415	    case FS_OPEN_FONT:
1416		blockrec->errcode = fs_do_open_font(fpe, blockrec);
1417		break;
1418	    case FS_LOAD_GLYPHS:
1419		blockrec->errcode = fs_read_glyphs(fpe, blockrec);
1420		break;
1421	    case FS_LIST_FONTS:
1422		blockrec->errcode = fs_read_list(fpe, blockrec);
1423		break;
1424	    case FS_LIST_WITH_INFO:
1425		blockrec->errcode = fs_read_list_info(fpe, blockrec);
1426		break;
1427	    default:
1428		break;
1429	    }
1430	    err = blockrec->errcode;
1431	    if (err != StillWorking)
1432	    {
1433		while (blockrec)
1434		{
1435		    blockrec->errcode = err;
1436		    if (client != blockrec->client)
1437			ClientSignal(blockrec->client);
1438		    blockrec = blockrec->depending;
1439		}
1440		_fs_unmark_block (conn, FS_PENDING_REPLY);
1441	    }
1442	}
1443	if (fs_reply_ready (conn))
1444	    _fs_mark_block (conn, FS_COMPLETE_REPLY);
1445	else
1446	    _fs_unmark_block (conn, FS_COMPLETE_REPLY);
1447    }
1448}
1449
1450static void
1451fs_fd_handler(int fd, void *data)
1452{
1453    FontPathElementPtr	fpe = data;
1454    FSFpePtr	    	conn = (FSFpePtr) fpe->private;
1455
1456    /*
1457     * Don't continue if the fd is -1 (which will be true when the
1458     * font server terminates
1459     */
1460    if ((conn->blockState & FS_RECONNECTING))
1461	_fs_check_reconnect (conn);
1462    else if ((conn->fs_fd != -1))
1463	fs_read_reply (fpe, 0);
1464}
1465
1466static int
1467fs_wakeup(FontPathElementPtr fpe)
1468{
1469    FSFpePtr	    conn = (FSFpePtr) fpe->private;
1470
1471    if ((conn->blockState & FS_RECONNECTING))
1472	_fs_check_reconnect (conn);
1473    if (conn->blockState & (FS_PENDING_REPLY|FS_BROKEN_CONNECTION|FS_BROKEN_WRITE))
1474	_fs_do_blocked (conn);
1475    if (conn->blockState & FS_COMPLETE_REPLY)
1476	fs_read_reply (fpe, 0);
1477#ifdef DEBUG
1478    {
1479	FSBlockDataPtr	    blockrec;
1480	FSBlockedFontPtr    bfont;
1481	static CARD32	    lastState;
1482	static FSBlockDataPtr	lastBlock;
1483
1484	if (conn->blockState || conn->blockedRequests || lastState || lastBlock)
1485	{
1486	    fprintf (stderr, "  Block State 0x%x\n", (int) conn->blockState);
1487	    lastState = conn->blockState;
1488	    lastBlock = conn->blockedRequests;
1489	}
1490	for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
1491	{
1492	    switch (blockrec->type) {
1493	    case FS_OPEN_FONT:
1494		bfont = (FSBlockedFontPtr) blockrec->data;
1495		fprintf (stderr, "  Blocked font errcode %d sequence %d state %s %s\n",
1496			 blockrec->errcode,
1497			 blockrec->sequenceNumber,
1498			 fs_open_states[bfont->state],
1499			 bfont->pfont ?
1500			 ((FSFontDataPtr) (bfont->pfont->fpePrivate))->name :
1501			 "<freed>");
1502		break;
1503	    case FS_LIST_FONTS:
1504		fprintf (stderr, "  Blocked list errcode %d sequence %d\n",
1505			 blockrec->errcode, blockrec->sequenceNumber);
1506		break;
1507	    default:
1508		fprintf (stderr, "  Blocked type %d errcode %d sequence %d\n",
1509			 blockrec->type,
1510			 blockrec->errcode,
1511			 blockrec->sequenceNumber);
1512		break;
1513	    }
1514	}
1515    }
1516#endif
1517    return FALSE;
1518}
1519
1520/*
1521 * Notice a dead connection and prepare for reconnect
1522 */
1523
1524void
1525_fs_connection_died(FSFpePtr conn)
1526{
1527    if (conn->blockState & FS_BROKEN_CONNECTION)
1528	return;
1529    fs_close_conn(conn);
1530    conn->brokenConnectionTime = GetTimeInMillis ();
1531    _fs_mark_block (conn, FS_BROKEN_CONNECTION);
1532    _fs_unmark_block (conn, FS_BROKEN_WRITE|FS_PENDING_WRITE|FS_RECONNECTING);
1533}
1534
1535/*
1536 * Signal clients that the connection has come back up
1537 */
1538static int
1539_fs_restart_connection(FSFpePtr conn)
1540{
1541    FSBlockDataPtr block;
1542
1543    _fs_unmark_block (conn, FS_GIVE_UP);
1544    while ((block = (FSBlockDataPtr) conn->blockedRequests))
1545    {
1546	if (block->errcode == StillWorking)
1547	{
1548	    ClientSignal(block->client);
1549	    fs_abort_blockrec(conn, block);
1550	}
1551    }
1552    return TRUE;
1553}
1554
1555/*
1556 * Declare this font server connection useless
1557 */
1558static void
1559_fs_giveup (FSFpePtr conn)
1560{
1561    FSBlockDataPtr  block;
1562
1563    if (conn->blockState & FS_GIVE_UP)
1564	return;
1565#ifdef DEBUG
1566    fprintf (stderr, "give up on FS \"%s\"\n", conn->servername);
1567#endif
1568    _fs_mark_block (conn, FS_GIVE_UP);
1569    while ((block = (FSBlockDataPtr) conn->blockedRequests))
1570    {
1571	if (block->errcode == StillWorking)
1572	{
1573	    ClientSignal (block->client);
1574	    fs_abort_blockrec (conn, block);
1575	}
1576    }
1577    if (conn->fs_fd >= 0)
1578	_fs_connection_died (conn);
1579}
1580
1581static void
1582_fs_do_blocked (FSFpePtr conn)
1583{
1584    CARD32      now;
1585
1586    now = GetTimeInMillis ();
1587    if ((conn->blockState & FS_PENDING_REPLY) &&
1588	TimeCmp (conn->blockedReplyTime, <=, now))
1589    {
1590	_fs_giveup (conn);
1591    }
1592    else
1593    {
1594	if (conn->blockState & FS_BROKEN_CONNECTION)
1595	{
1596	    /* Try to reconnect broken connections */
1597	    if (TimeCmp (conn->brokenConnectionTime, <=, now))
1598		_fs_start_reconnect (conn);
1599	}
1600	else if (conn->blockState & FS_BROKEN_WRITE)
1601	{
1602	    /* Try to flush blocked connections */
1603	    if (TimeCmp (conn->brokenWriteTime, <=, now))
1604		_fs_flush (conn);
1605	}
1606    }
1607}
1608
1609/*
1610 * sends the actual request out
1611 */
1612/* ARGSUSED */
1613static int
1614fs_send_open_font(pointer client, FontPathElementPtr fpe, Mask flags,
1615		  const char *name, int namelen,
1616		  fsBitmapFormat format, fsBitmapFormatMask fmask,
1617		  XID id, FontPtr *ppfont)
1618{
1619    FSFpePtr		    conn = (FSFpePtr) fpe->private;
1620    FontPtr		    font;
1621    FSBlockDataPtr	    blockrec = NULL;
1622    FSBlockedFontPtr	    bfont;
1623    FSFontDataPtr	    fsd;
1624    fsOpenBitmapFontReq	    openreq;
1625    fsQueryXInfoReq	    inforeq;
1626    fsQueryXExtents16Req    extreq;
1627    int			    err;
1628    unsigned char	    buf[1024];
1629
1630    if (conn->blockState & FS_GIVE_UP)
1631	return BadFontName;
1632
1633    if (namelen < 0 || namelen > sizeof (buf) - 1)
1634	return BadFontName;
1635
1636    /*
1637     * Get the font structure put together, either by reusing
1638     * the existing one or creating a new one
1639     */
1640    if (flags & FontReopen)
1641    {
1642	Atom	nameatom, fn = None;
1643	int	i;
1644
1645	font = *ppfont;
1646	fsd = (FSFontDataPtr)font->fpePrivate;
1647	/* This is an attempt to reopen a font.  Did the font have a
1648	   NAME property? */
1649	if ((nameatom = MakeAtom("FONT", 4, 0)) != None)
1650	{
1651	    for (i = 0; i < font->info.nprops; i++)
1652		if (font->info.props[i].name == nameatom &&
1653		    font->info.isStringProp[i])
1654		{
1655		    fn = font->info.props[i].value;
1656		    break;
1657		}
1658	}
1659	if (fn == None || !(name = NameForAtom(fn)))
1660	{
1661	    name = fsd->name;
1662	    namelen = fsd->namelen;
1663	}
1664	else
1665	    namelen = strlen(name);
1666    }
1667    else
1668    {
1669	font = fs_create_font (fpe, name, namelen, format, fmask);
1670	if (!font)
1671	    return AllocError;
1672
1673	fsd = (FSFontDataPtr)font->fpePrivate;
1674    }
1675
1676    /* make a new block record, and add it to the end of the list */
1677    blockrec = fs_new_block_rec(font->fpe, client, FS_OPEN_FONT);
1678    if (!blockrec)
1679    {
1680	if (!(flags & FontReopen))
1681	    (*font->unload_font) (font);
1682	return AllocError;
1683    }
1684
1685    fsd->generation = conn->generation;
1686
1687    bfont = (FSBlockedFontPtr) blockrec->data;
1688    bfont->fontid = fsd->fontid;
1689    bfont->pfont = font;
1690    bfont->state = FS_OPEN_REPLY;
1691    bfont->flags = flags;
1692    bfont->format = fsd->format;
1693    bfont->clients_depending = (FSClientsDependingPtr)0;
1694    bfont->freeFont = (flags & FontReopen) == 0;
1695
1696    /*
1697     * Must check this before generating any protocol, otherwise we'll
1698     * mess up a reconnect in progress
1699     */
1700    if (conn->blockState & (FS_BROKEN_CONNECTION | FS_RECONNECTING))
1701    {
1702	_fs_pending_reply (conn);
1703	return Suspended;
1704    }
1705
1706    _fs_client_access (conn, client, (flags & FontOpenSync) != 0);
1707    _fs_client_resolution(conn);
1708
1709    /* do an FS_OpenFont, FS_QueryXInfo and FS_QueryXExtents */
1710    buf[0] = (unsigned char) namelen;
1711    memcpy(&buf[1], name, namelen);
1712    openreq.reqType = FS_OpenBitmapFont;
1713    openreq.pad = 0;
1714    openreq.fid = fsd->fontid;
1715    openreq.format_hint = fsd->format;
1716    openreq.format_mask = fsd->fmask;
1717    openreq.length = (SIZEOF(fsOpenBitmapFontReq) + namelen + 4) >> 2;
1718
1719    _fs_add_req_log(conn, FS_OpenBitmapFont);
1720    _fs_write(conn, (char *) &openreq, SIZEOF(fsOpenBitmapFontReq));
1721    _fs_write_pad(conn, (char *) buf, namelen + 1);
1722
1723    blockrec->sequenceNumber = conn->current_seq;
1724
1725    inforeq.reqType = FS_QueryXInfo;
1726    inforeq.pad = 0;
1727    inforeq.id = fsd->fontid;
1728    inforeq.length = SIZEOF(fsQueryXInfoReq) >> 2;
1729
1730    bfont->queryInfoSequence = conn->current_seq + 1;
1731
1732    _fs_add_req_log(conn, FS_QueryXInfo);
1733    _fs_write(conn, (char *) &inforeq, SIZEOF(fsQueryXInfoReq));
1734
1735    if (!(bfont->flags & FontReopen))
1736    {
1737	extreq.reqType = FS_QueryXExtents16;
1738	extreq.range = fsTrue;
1739	extreq.fid = fsd->fontid;
1740	extreq.num_ranges = 0;
1741	extreq.length = SIZEOF(fsQueryXExtents16Req) >> 2;
1742
1743	bfont->queryExtentsSequence = conn->current_seq + 1;
1744
1745	_fs_add_req_log(conn, FS_QueryXExtents16);
1746	_fs_write(conn, (char *) &extreq, SIZEOF(fsQueryXExtents16Req));
1747    }
1748
1749    _fs_prepare_for_reply (conn);
1750
1751    err = blockrec->errcode;
1752    if (bfont->flags & FontOpenSync)
1753    {
1754	while (blockrec->errcode == StillWorking)
1755	{
1756	    if (fs_await_reply (conn) != FSIO_READY)
1757	    {
1758		blockrec->errcode = BadFontName;
1759		break;
1760	    }
1761	    fs_read_reply (font->fpe, client);
1762	}
1763	err = blockrec->errcode;
1764	if (err == Successful)
1765	    *ppfont = bfont->pfont;
1766	else
1767	    fs_cleanup_bfont (conn, bfont);
1768	bfont->freeFont = FALSE;
1769	_fs_remove_block_rec (conn, blockrec);
1770    }
1771    return err == StillWorking ? Suspended : err;
1772}
1773
1774static void
1775fs_send_query_bitmaps(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
1776{
1777    FSFpePtr		    conn = (FSFpePtr) fpe->private;
1778    FSBlockedFontPtr	    bfont = (FSBlockedFontPtr) blockrec->data;
1779    fsQueryXBitmaps16Req    bitreq;
1780
1781    /* send the request */
1782    bitreq.reqType = FS_QueryXBitmaps16;
1783    bitreq.fid = bfont->fontid;
1784    bitreq.format = bfont->format;
1785    bitreq.range = TRUE;
1786    bitreq.length = SIZEOF(fsQueryXBitmaps16Req) >> 2;
1787    bitreq.num_ranges = 0;
1788
1789    bfont->queryBitmapsSequence = conn->current_seq + 1;
1790
1791    _fs_add_req_log(conn, FS_QueryXBitmaps16);
1792    _fs_write(conn, (char *) &bitreq, SIZEOF(fsQueryXBitmaps16Req));
1793}
1794
1795/* ARGSUSED */
1796static int
1797fs_open_font(pointer client, FontPathElementPtr fpe, Mask flags,
1798	     const char *name, int namelen,
1799	     fsBitmapFormat format, fsBitmapFormatMask fmask,
1800	     XID id, FontPtr *ppfont,
1801	     char **alias, FontPtr non_cachable_font)
1802{
1803    FSFpePtr		conn = (FSFpePtr) fpe->private;
1804    FSBlockDataPtr	blockrec;
1805    FSBlockedFontPtr	bfont;
1806    int			err;
1807
1808    /* libfont interface expects ImageRectMin glyphs */
1809    format = (format & ~BitmapFormatImageRectMask) | BitmapFormatImageRectMin;
1810
1811    *alias = (char *) 0;
1812    for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
1813    {
1814	if (blockrec->type == FS_OPEN_FONT && blockrec->client == client)
1815	{
1816	    err = blockrec->errcode;
1817	    if (err == StillWorking)
1818		return Suspended;
1819
1820	    bfont = (FSBlockedFontPtr) blockrec->data;
1821	    if (err == Successful)
1822		*ppfont = bfont->pfont;
1823	    else
1824		fs_cleanup_bfont (conn, bfont);
1825	    _fs_remove_block_rec (conn, blockrec);
1826	    return err;
1827	}
1828    }
1829    return fs_send_open_font(client, fpe, flags, name, namelen, format, fmask,
1830			     id, ppfont);
1831}
1832
1833/* ARGSUSED */
1834static int
1835fs_send_close_font(FSFpePtr conn, Font id)
1836{
1837    fsCloseReq  req;
1838
1839    if (conn->blockState & FS_GIVE_UP)
1840	return Successful;
1841    /* tell the font server to close the font */
1842    req.reqType = FS_CloseFont;
1843    req.pad = 0;
1844    req.length = SIZEOF(fsCloseReq) >> 2;
1845    req.id = id;
1846    _fs_add_req_log(conn, FS_CloseFont);
1847    _fs_write(conn, (char *) &req, SIZEOF(fsCloseReq));
1848
1849    return Successful;
1850}
1851
1852/* ARGSUSED */
1853static void
1854fs_close_font(FontPathElementPtr fpe, FontPtr pfont)
1855{
1856    FSFontDataPtr   fsd = (FSFontDataPtr) pfont->fpePrivate;
1857    FSFpePtr	    conn = (FSFpePtr) fpe->private;
1858
1859    if (conn->generation == fsd->generation)
1860	fs_send_close_font(conn, fsd->fontid);
1861
1862#ifdef DEBUG
1863    {
1864	FSBlockDataPtr	    blockrec;
1865	FSBlockedFontPtr    bfont;
1866
1867	for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
1868	{
1869	    if (blockrec->type == FS_OPEN_FONT)
1870	    {
1871		bfont = (FSBlockedFontPtr) blockrec->data;
1872		if (bfont->pfont == pfont)
1873		    fprintf (stderr, "closing font which hasn't been opened\n");
1874	    }
1875	}
1876    }
1877#endif
1878    (*pfont->unload_font) (pfont);
1879}
1880
1881static int
1882fs_read_glyphs(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
1883{
1884    FSBlockedGlyphPtr	    bglyph = (FSBlockedGlyphPtr) blockrec->data;
1885    FSBlockedFontPtr	    bfont = (FSBlockedFontPtr) blockrec->data;
1886    FSFpePtr		    conn = (FSFpePtr) fpe->private;
1887    FontPtr		    pfont = bglyph->pfont;
1888					/* works for either blocked font
1889					   or glyph rec...  pfont is at
1890					   the very beginning of both
1891					   blockrec->data structures */
1892    FSFontDataPtr	    fsd = (FSFontDataPtr) (pfont->fpePrivate);
1893    FSFontPtr		    fsdata = (FSFontPtr) pfont->fontPrivate;
1894    FontInfoPtr		    pfi = &pfont->info;
1895    fsQueryXBitmaps16Reply  *rep;
1896    char		    *buf;
1897    long		    bufleft; /* length of reply left to use */
1898    fsOffset32		    *ppbits;
1899    fsOffset32		    local_off;
1900    char		    *off_adr;
1901    pointer		    pbitmaps;
1902    char		    *bits, *allbits;
1903#ifdef DEBUG
1904    char		    *origallbits;
1905#endif
1906    int			    i,
1907			    err;
1908    int			    nranges = 0;
1909    int			    ret;
1910    fsRange		    *nextrange = 0;
1911    unsigned long	    minchar, maxchar;
1912
1913    rep = (fsQueryXBitmaps16Reply *) fs_get_reply (conn, &ret);
1914    if (!rep || rep->type == FS_Error ||
1915	(rep->length < LENGTHOF(fsQueryXBitmaps16Reply)))
1916    {
1917	if (ret == FSIO_BLOCK)
1918	    return StillWorking;
1919	if (rep)
1920	    _fs_done_read (conn, rep->length << 2);
1921	err = AllocError;
1922	_fs_reply_failed (rep, fsQueryXBitmaps16Reply, "<");
1923	goto bail;
1924    }
1925
1926    buf = (char *) rep;
1927    buf += SIZEOF (fsQueryXBitmaps16Reply);
1928
1929    bufleft = rep->length << 2;
1930    bufleft -= SIZEOF (fsQueryXBitmaps16Reply);
1931
1932    if ((bufleft / SIZEOF (fsOffset32)) < rep->num_chars)
1933    {
1934#ifdef DEBUG
1935	fprintf(stderr,
1936		"fsQueryXBitmaps16: num_chars (%u) > bufleft (%ld) / %d\n",
1937		(unsigned) rep->num_chars, bufleft, SIZEOF (fsOffset32));
1938#endif
1939	err = AllocError;
1940	goto bail;
1941    }
1942    ppbits = (fsOffset32 *) buf;
1943    buf += SIZEOF (fsOffset32) * (rep->num_chars);
1944    bufleft -= SIZEOF (fsOffset32) * (rep->num_chars);
1945
1946    if (bufleft < rep->nbytes)
1947    {
1948#ifdef DEBUG
1949	fprintf(stderr,
1950		"fsQueryXBitmaps16: nbytes (%u) > bufleft (%ld)\n",
1951		(unsigned) rep->nbytes, bufleft);
1952#endif
1953	err = AllocError;
1954	goto bail;
1955    }
1956    pbitmaps = (pointer ) buf;
1957
1958    if (blockrec->type == FS_LOAD_GLYPHS)
1959    {
1960	nranges = bglyph->num_expected_ranges;
1961	nextrange = bglyph->expected_ranges;
1962    }
1963
1964    /* place the incoming glyphs */
1965    if (nranges)
1966    {
1967	/* We're operating under the assumption that the ranges
1968	   requested in the LoadGlyphs call were all legal for this
1969	   font, and that individual ranges do not cover multiple
1970	   rows...  fs_build_range() is designed to ensure this. */
1971	minchar = (nextrange->min_char_high - pfi->firstRow) *
1972		  (pfi->lastCol - pfi->firstCol + 1) +
1973		  nextrange->min_char_low - pfi->firstCol;
1974	maxchar = (nextrange->max_char_high - pfi->firstRow) *
1975		  (pfi->lastCol - pfi->firstCol + 1) +
1976		  nextrange->max_char_low - pfi->firstCol;
1977	nextrange++;
1978    }
1979    else
1980    {
1981	minchar = 0;
1982	maxchar = rep->num_chars;
1983    }
1984
1985    off_adr = (char *)ppbits;
1986
1987    allbits = fs_alloc_glyphs (pfont, rep->nbytes);
1988
1989    if (!allbits)
1990    {
1991	err = AllocError;
1992	goto bail;
1993    }
1994
1995#ifdef DEBUG
1996    origallbits = allbits;
1997    fprintf (stderr, "Reading %d glyphs in %d bytes for %s\n",
1998	     (int) rep->num_chars, (int) rep->nbytes, fsd->name);
1999#endif
2000
2001    for (i = 0; i < rep->num_chars; i++)
2002    {
2003	memcpy(&local_off, off_adr, SIZEOF(fsOffset32));	/* align it */
2004	if (blockrec->type == FS_OPEN_FONT ||
2005	    fsdata->encoding[minchar].bits == &_fs_glyph_requested)
2006	{
2007	    /*
2008	     * Broken X font server returns bits for missing characters
2009	     * when font is padded
2010	     */
2011	    if (NONZEROMETRICS(&fsdata->encoding[minchar].metrics))
2012	    {
2013		if (local_off.length &&
2014		    (local_off.position < rep->nbytes) &&
2015		    (local_off.length <= (rep->nbytes - local_off.position)))
2016		{
2017		    bits = allbits;
2018		    allbits += local_off.length;
2019		    memcpy(bits, (char *)pbitmaps + local_off.position,
2020			   local_off.length);
2021		}
2022		else
2023		    bits = &_fs_glyph_zero_length;
2024	    }
2025	    else
2026		bits = 0;
2027	    if (fsdata->encoding[minchar].bits == &_fs_glyph_requested)
2028		fsd->glyphs_to_get--;
2029	    fsdata->encoding[minchar].bits = bits;
2030	}
2031	if (minchar++ == maxchar)
2032	{
2033	    if (!--nranges) break;
2034	    minchar = (nextrange->min_char_high - pfi->firstRow) *
2035		      (pfi->lastCol - pfi->firstCol + 1) +
2036		      nextrange->min_char_low - pfi->firstCol;
2037	    maxchar = (nextrange->max_char_high - pfi->firstRow) *
2038		      (pfi->lastCol - pfi->firstCol + 1) +
2039		      nextrange->max_char_low - pfi->firstCol;
2040	    nextrange++;
2041	}
2042	off_adr += SIZEOF(fsOffset32);
2043    }
2044#ifdef DEBUG
2045    fprintf (stderr, "Used %d bytes instead of %d\n",
2046	     (int) (allbits - origallbits), (int) rep->nbytes);
2047#endif
2048
2049    if (blockrec->type == FS_OPEN_FONT)
2050    {
2051	fsd->glyphs_to_get = 0;
2052	bfont->state = FS_DONE_REPLY;
2053    }
2054    err = Successful;
2055
2056bail:
2057    if (rep)
2058	_fs_done_read (conn, rep->length << 2);
2059    return err;
2060}
2061
2062static int
2063fs_send_load_glyphs(pointer client, FontPtr pfont,
2064		    int nranges, fsRange *ranges)
2065{
2066    FontPathElementPtr	    fpe = pfont->fpe;
2067    FSFpePtr		    conn = (FSFpePtr) fpe->private;
2068    FSBlockedGlyphPtr	    blockedglyph;
2069    fsQueryXBitmaps16Req    req;
2070    FSBlockDataPtr	    blockrec;
2071
2072    if (conn->blockState & FS_GIVE_UP)
2073	return BadCharRange;
2074
2075    /* make a new block record, and add it to the end of the list */
2076    blockrec = fs_new_block_rec(fpe, client, FS_LOAD_GLYPHS);
2077    if (!blockrec)
2078	return AllocError;
2079    blockedglyph = (FSBlockedGlyphPtr) blockrec->data;
2080    blockedglyph->pfont = pfont;
2081    blockedglyph->num_expected_ranges = nranges;
2082    /* Assumption: it's our job to free ranges */
2083    blockedglyph->expected_ranges = ranges;
2084    blockedglyph->clients_depending = (FSClientsDependingPtr)0;
2085
2086    if (conn->blockState & (FS_BROKEN_CONNECTION|FS_RECONNECTING))
2087    {
2088	_fs_pending_reply (conn);
2089	return Suspended;
2090    }
2091
2092    /* send the request */
2093    req.reqType = FS_QueryXBitmaps16;
2094    req.fid = ((FSFontDataPtr) pfont->fpePrivate)->fontid;
2095    req.format = pfont->format;
2096    if (pfont->info.terminalFont)
2097	req.format = (req.format & ~(BitmapFormatImageRectMask)) |
2098		     BitmapFormatImageRectMax;
2099    req.range = TRUE;
2100    /* each range takes up 4 bytes */
2101    req.length = (SIZEOF(fsQueryXBitmaps16Req) >> 2) + nranges;
2102    req.num_ranges = nranges * 2;	/* protocol wants count of fsChar2bs */
2103    _fs_add_req_log(conn, FS_QueryXBitmaps16);
2104    _fs_write(conn, (char *) &req, SIZEOF(fsQueryXBitmaps16Req));
2105
2106    blockrec->sequenceNumber = conn->current_seq;
2107
2108    /* Send ranges to the server... pack into a char array by hand
2109       to avoid structure-packing portability problems and to
2110       handle swapping for version1 protocol */
2111    if (nranges)
2112    {
2113#define RANGE_BUFFER_SIZE 64
2114#define RANGE_BUFFER_SIZE_MASK 63
2115	int i;
2116	char range_buffer[RANGE_BUFFER_SIZE * 4];
2117	char *range_buffer_p;
2118
2119	range_buffer_p = range_buffer;
2120	for (i = 0; i < nranges;)
2121	{
2122	    if (conn->fsMajorVersion > 1)
2123	    {
2124		*range_buffer_p++ = ranges[i].min_char_high;
2125		*range_buffer_p++ = ranges[i].min_char_low;
2126		*range_buffer_p++ = ranges[i].max_char_high;
2127		*range_buffer_p++ = ranges[i].max_char_low;
2128	    }
2129	    else
2130	    {
2131		*range_buffer_p++ = ranges[i].min_char_low;
2132		*range_buffer_p++ = ranges[i].min_char_high;
2133		*range_buffer_p++ = ranges[i].max_char_low;
2134		*range_buffer_p++ = ranges[i].max_char_high;
2135	    }
2136
2137	    if (!(++i & RANGE_BUFFER_SIZE_MASK))
2138	    {
2139		_fs_write(conn, range_buffer, RANGE_BUFFER_SIZE * 4);
2140		range_buffer_p = range_buffer;
2141	    }
2142	}
2143	if (i &= RANGE_BUFFER_SIZE_MASK)
2144	    _fs_write(conn, range_buffer, i * 4);
2145    }
2146
2147    _fs_prepare_for_reply (conn);
2148    return Suspended;
2149}
2150
2151static int
2152_fs_load_glyphs(pointer client, FontPtr pfont, Bool range_flag,
2153		unsigned int nchars, int item_size, unsigned char *data)
2154{
2155    FSFpePtr		    conn = (FSFpePtr) pfont->fpe->private;
2156    int			    nranges = 0;
2157    fsRange		    *ranges = NULL;
2158    int			    res;
2159    FSBlockDataPtr	    blockrec;
2160    FSBlockedGlyphPtr	    blockedglyph;
2161    FSClientsDependingPtr   *clients_depending = NULL;
2162    int			    err;
2163
2164    /* see if the result is already there */
2165    for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
2166    {
2167	if (blockrec->type == FS_LOAD_GLYPHS)
2168	{
2169	    blockedglyph = (FSBlockedGlyphPtr) blockrec->data;
2170	    if (blockedglyph->pfont == pfont)
2171	    {
2172		/* Look for this request */
2173		if (blockrec->client == client)
2174		{
2175		    err = blockrec->errcode;
2176		    if (err == StillWorking)
2177			return Suspended;
2178		    _fs_signal_clients_depending(&blockedglyph->clients_depending);
2179		    _fs_remove_block_rec(conn, blockrec);
2180		    return err;
2181		}
2182		/* We've found an existing LoadGlyphs blockrec for this
2183		   font but for another client.  Rather than build a
2184		   blockrec for it now (which entails some complex
2185		   maintenance), we'll add it to a queue of clients to
2186		   be signalled when the existing LoadGlyphs is
2187		   completed.  */
2188		clients_depending = &blockedglyph->clients_depending;
2189		break;
2190	    }
2191	}
2192	else if (blockrec->type == FS_OPEN_FONT)
2193	{
2194	    FSBlockedFontPtr bfont;
2195	    bfont = (FSBlockedFontPtr) blockrec->data;
2196	    if (bfont->pfont == pfont)
2197	    {
2198		/*
2199		 * An OpenFont is pending for this font, this must
2200		 * be from a reopen attempt, so finish the open
2201		 * attempt and retry the LoadGlyphs
2202		 */
2203		if (blockrec->client == client)
2204		{
2205		    err = blockrec->errcode;
2206		    if (err == StillWorking)
2207			return Suspended;
2208
2209		    _fs_signal_clients_depending(&bfont->clients_depending);
2210		    _fs_remove_block_rec(conn, blockrec);
2211		    if (err != Successful)
2212			return err;
2213		    break;
2214		}
2215		/* We've found an existing OpenFont blockrec for this
2216		   font but for another client.  Rather than build a
2217		   blockrec for it now (which entails some complex
2218		   maintenance), we'll add it to a queue of clients to
2219		   be signalled when the existing OpenFont is
2220		   completed.  */
2221		if (blockrec->errcode == StillWorking)
2222		{
2223		    clients_depending = &bfont->clients_depending;
2224		    break;
2225		}
2226	    }
2227	}
2228    }
2229
2230    /*
2231     * see if the desired glyphs already exist, and return Successful if they
2232     * do, otherwise build up character range/character string
2233     */
2234    res = fs_build_range(pfont, range_flag, nchars, item_size, data,
2235			 &nranges, &ranges);
2236
2237    switch (res)
2238    {
2239	case AccessDone:
2240	    return Successful;
2241
2242	case Successful:
2243	    break;
2244
2245	default:
2246	    return res;
2247    }
2248
2249    /*
2250     * If clients_depending is not null, this request must wait for
2251     * some prior request(s) to complete.
2252     */
2253    if (clients_depending)
2254    {
2255	/* Since we're not ready to send the load_glyphs request yet,
2256	   clean up the damage (if any) caused by the fs_build_range()
2257	   call. */
2258	if (nranges)
2259	{
2260	    _fs_clean_aborted_loadglyphs(pfont, nranges, ranges);
2261	    free(ranges);
2262	}
2263	return _fs_add_clients_depending(clients_depending, client);
2264    }
2265
2266    /*
2267     * If fsd->generation != conn->generation, the font has been closed
2268     * due to a lost connection.  We will reopen it, which will result
2269     * in one of three things happening:
2270     *	 1) The open will succeed and obtain the same font.  Life
2271     *	    is wonderful.
2272     *	 2) The open will fail.  There is code above to recognize this
2273     *	    and flunk the LoadGlyphs request.  The client might not be
2274     *	    thrilled.
2275     *	 3) Worst case: the open will succeed but the font we open will
2276     *	    be different.  The fs_read_query_info() procedure attempts
2277     *	    to detect this by comparing the existing metrics and
2278     *	    properties against those of the reopened font... if they
2279     *	    don't match, we flunk the reopen, which eventually results
2280     *	    in flunking the LoadGlyphs request.  We could go a step
2281     *	    further and compare the extents, but this should be
2282     *	    sufficient.
2283     */
2284    if (((FSFontDataPtr)pfont->fpePrivate)->generation != conn->generation)
2285    {
2286	/* Since we're not ready to send the load_glyphs request yet,
2287	   clean up the damage caused by the fs_build_range() call. */
2288	_fs_clean_aborted_loadglyphs(pfont, nranges, ranges);
2289	free(ranges);
2290
2291	/* Now try to reopen the font. */
2292	return fs_send_open_font(client, pfont->fpe,
2293				 (Mask)FontReopen, (char *)0, 0,
2294				 (fsBitmapFormat)0, (fsBitmapFormatMask)0,
2295				 (XID)0, &pfont);
2296    }
2297
2298    return fs_send_load_glyphs(client, pfont, nranges, ranges);
2299}
2300
2301int
2302fs_load_all_glyphs(FontPtr pfont)
2303{
2304    int		err;
2305    FSFpePtr	conn = (FSFpePtr) pfont->fpe->private;
2306
2307    /*
2308     * The purpose of this procedure is to load all glyphs in the event
2309     * that we're dealing with someone who doesn't understand the finer
2310     * points of glyph caching...  it is called from _fs_get_glyphs() if
2311     * the latter is called to get glyphs that have not yet been loaded.
2312     * We assume that the caller will not know how to handle a return
2313     * value of Suspended (usually the case for a GetGlyphs() caller),
2314     * so this procedure hangs around, freezing the server, for the
2315     * request to complete.  This is an unpleasant kluge called to
2316     * perform an unpleasant job that, we hope, will never be required.
2317     */
2318
2319    while ((err = _fs_load_glyphs(__GetServerClient(), pfont, TRUE, 0, 0, NULL)) ==
2320	   Suspended)
2321    {
2322	if (fs_await_reply (conn) != FSIO_READY)
2323	{
2324	    /* Get rid of blockrec */
2325	    fs_client_died(__GetServerClient(), pfont->fpe);
2326	    err = BadCharRange;
2327	    break;
2328	}
2329	fs_read_reply (pfont->fpe, __GetServerClient());
2330    }
2331    return err;
2332}
2333
2334static int
2335fs_read_list(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
2336{
2337    FSFpePtr		conn = (FSFpePtr) fpe->private;
2338    FSBlockedListPtr	blist = (FSBlockedListPtr) blockrec->data;
2339    fsListFontsReply	*rep;
2340    char		*data;
2341    long		dataleft; /* length of reply left to use */
2342    int			length,
2343			i,
2344			ret;
2345    int			err;
2346
2347    rep = (fsListFontsReply *) fs_get_reply (conn, &ret);
2348    if (!rep || rep->type == FS_Error ||
2349	(rep->length < LENGTHOF(fsListFontsReply)))
2350    {
2351	if (ret == FSIO_BLOCK)
2352	    return StillWorking;
2353	if (rep)
2354	    _fs_done_read (conn, rep->length << 2);
2355	_fs_reply_failed (rep, fsListFontsReply, "<");
2356	return AllocError;
2357    }
2358    data = (char *) rep + SIZEOF (fsListFontsReply);
2359    dataleft = (rep->length << 2) - SIZEOF (fsListFontsReply);
2360
2361    err = Successful;
2362    /* copy data into FontPathRecord */
2363    for (i = 0; i < rep->nFonts; i++)
2364    {
2365	if (dataleft < 1)
2366	    break;
2367	length = *(unsigned char *)data++;
2368	dataleft--; /* used length byte */
2369	if (length > dataleft) {
2370#ifdef DEBUG
2371	    fprintf(stderr,
2372		    "fsListFonts: name length (%d) > dataleft (%ld)\n",
2373		    length, dataleft);
2374#endif
2375	    err = BadFontName;
2376	    break;
2377	}
2378	err = xfont2_add_font_names_name(blist->names, data, length);
2379	if (err != Successful)
2380	    break;
2381	data += length;
2382	dataleft -= length;
2383    }
2384    _fs_done_read (conn, rep->length << 2);
2385    return err;
2386}
2387
2388static int
2389fs_send_list_fonts(pointer client, FontPathElementPtr fpe, const char *pattern,
2390		   int patlen, int maxnames, FontNamesPtr newnames)
2391{
2392    FSFpePtr		conn = (FSFpePtr) fpe->private;
2393    FSBlockDataPtr	blockrec;
2394    FSBlockedListPtr	blockedlist;
2395    fsListFontsReq	req;
2396
2397    if (conn->blockState & FS_GIVE_UP)
2398	return BadFontName;
2399
2400    /* make a new block record, and add it to the end of the list */
2401    blockrec = fs_new_block_rec(fpe, client, FS_LIST_FONTS);
2402    if (!blockrec)
2403	return AllocError;
2404    blockedlist = (FSBlockedListPtr) blockrec->data;
2405    blockedlist->names = newnames;
2406
2407    if (conn->blockState & (FS_BROKEN_CONNECTION | FS_RECONNECTING))
2408    {
2409	_fs_pending_reply (conn);
2410	return Suspended;
2411    }
2412
2413    _fs_client_access (conn, client, FALSE);
2414    _fs_client_resolution(conn);
2415
2416    /* send the request */
2417    req.reqType = FS_ListFonts;
2418    req.pad = 0;
2419    req.maxNames = maxnames;
2420    req.nbytes = patlen;
2421    req.length = (SIZEOF(fsListFontsReq) + patlen + 3) >> 2;
2422    _fs_add_req_log(conn, FS_ListFonts);
2423    _fs_write(conn, (char *) &req, SIZEOF(fsListFontsReq));
2424    _fs_write_pad(conn, (char *) pattern, patlen);
2425
2426    blockrec->sequenceNumber = conn->current_seq;
2427
2428    _fs_prepare_for_reply (conn);
2429    return Suspended;
2430}
2431
2432static int
2433fs_list_fonts(pointer client, FontPathElementPtr fpe,
2434	      const char *pattern, int patlen, int maxnames, FontNamesPtr newnames)
2435{
2436    FSFpePtr		conn = (FSFpePtr) fpe->private;
2437    FSBlockDataPtr	blockrec;
2438    int			err;
2439
2440    /* see if the result is already there */
2441    for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
2442    {
2443	if (blockrec->type == FS_LIST_FONTS && blockrec->client == client)
2444	{
2445	    err = blockrec->errcode;
2446	    if (err == StillWorking)
2447		return Suspended;
2448	    _fs_remove_block_rec(conn, blockrec);
2449	    return err;
2450	}
2451    }
2452
2453    /* didn't find waiting record, so send a new one */
2454    return fs_send_list_fonts(client, fpe, pattern, patlen, maxnames, newnames);
2455}
2456
2457/*
2458 * Read a single list info reply and restart for the next reply
2459 */
2460static int
2461fs_read_list_info(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
2462{
2463    FSBlockedListInfoPtr	binfo = (FSBlockedListInfoPtr) blockrec->data;
2464    fsListFontsWithXInfoReply	*rep;
2465    char			*buf;
2466    long			bufleft;
2467    FSFpePtr			conn = (FSFpePtr) fpe->private;
2468    fsPropInfo			*pi;
2469    fsPropOffset		*po;
2470    pointer			pd;
2471    int				ret;
2472    int				err;
2473
2474    /* clean up anything from the last trip */
2475    _fs_free_props (&binfo->info);
2476
2477    rep = (fsListFontsWithXInfoReply *) fs_get_reply (conn, &ret);
2478    if (!rep || rep->type == FS_Error ||
2479	((rep->nameLength != 0) &&
2480	 (rep->length < LENGTHOF(fsListFontsWithXInfoReply))))
2481    {
2482	if (ret == FSIO_BLOCK)
2483	    return StillWorking;
2484	binfo->status = FS_LFWI_FINISHED;
2485	err = AllocError;
2486	_fs_reply_failed (rep, fsListFontsWithXInfoReply, "<");
2487	goto done;
2488    }
2489    /*
2490     * Normal termination -- the list ends with a name of length 0
2491     */
2492    if (rep->nameLength == 0)
2493    {
2494#ifdef DEBUG
2495	fprintf (stderr, "fs_read_list_info done\n");
2496#endif
2497	binfo->status = FS_LFWI_FINISHED;
2498	err = BadFontName;
2499	goto done;
2500    }
2501
2502    buf = (char *) rep + SIZEOF (fsListFontsWithXInfoReply);
2503    bufleft = (rep->length << 2) - SIZEOF (fsListFontsWithXInfoReply);
2504
2505    /*
2506     * The original FS implementation didn't match
2507     * the spec, version 1 was respecified to match the FS.
2508     * Version 2 matches the original intent
2509     */
2510    if (conn->fsMajorVersion <= 1)
2511    {
2512	if (rep->nameLength > bufleft) {
2513#ifdef DEBUG
2514	    fprintf(stderr,
2515		    "fsListFontsWithXInfo: name length (%d) > bufleft (%ld)\n",
2516		    (int) rep->nameLength, bufleft);
2517#endif
2518	    err = AllocError;
2519	    goto done;
2520	}
2521	/* binfo->name is a 256 char array, rep->nameLength is a CARD8 */
2522	memcpy (binfo->name, buf, rep->nameLength);
2523	buf += _fs_pad_length (rep->nameLength);
2524	bufleft -= _fs_pad_length (rep->nameLength);
2525    }
2526    pi = (fsPropInfo *) buf;
2527    if (SIZEOF (fsPropInfo) > bufleft) {
2528#ifdef DEBUG
2529	fprintf(stderr,
2530		"fsListFontsWithXInfo: PropInfo length (%d) > bufleft (%ld)\n",
2531		(int) SIZEOF (fsPropInfo), bufleft);
2532#endif
2533	err = AllocError;
2534	goto done;
2535    }
2536    bufleft -= SIZEOF (fsPropInfo);
2537    buf += SIZEOF (fsPropInfo);
2538    po = (fsPropOffset *) buf;
2539    if (pi->num_offsets > (bufleft / SIZEOF (fsPropOffset))) {
2540#ifdef DEBUG
2541	fprintf(stderr,
2542		"fsListFontsWithXInfo: offset length (%u * %d) > bufleft (%ld)\n",
2543		(unsigned) pi->num_offsets, (int) SIZEOF (fsPropOffset), bufleft);
2544#endif
2545	err = AllocError;
2546	goto done;
2547    }
2548    bufleft -= pi->num_offsets * SIZEOF (fsPropOffset);
2549    buf += pi->num_offsets * SIZEOF (fsPropOffset);
2550    pd = (pointer) buf;
2551    if (pi->data_len > bufleft) {
2552#ifdef DEBUG
2553	fprintf(stderr,
2554		"fsListFontsWithXInfo: data length (%u) > bufleft (%ld)\n",
2555		(unsigned) pi->data_len, bufleft);
2556#endif
2557	err = AllocError;
2558	goto done;
2559    }
2560    bufleft -= pi->data_len;
2561    buf += pi->data_len;
2562    if (conn->fsMajorVersion > 1)
2563    {
2564	if (rep->nameLength > bufleft) {
2565#ifdef DEBUG
2566	    fprintf(stderr,
2567		    "fsListFontsWithXInfo: name length (%d) > bufleft (%ld)\n",
2568		    (int) rep->nameLength, bufleft);
2569#endif
2570	    err = AllocError;
2571	    goto done;
2572	}
2573	/* binfo->name is a 256 char array, rep->nameLength is a CARD8 */
2574	memcpy (binfo->name, buf, rep->nameLength);
2575	buf += _fs_pad_length (rep->nameLength);
2576	bufleft -= _fs_pad_length (rep->nameLength);
2577    }
2578
2579#ifdef DEBUG
2580    binfo->name[rep->nameLength] = '\0';
2581    fprintf (stderr, "fs_read_list_info %s\n", binfo->name);
2582#endif
2583    err = _fs_convert_lfwi_reply(conn, &binfo->info, rep, pi, po, pd);
2584    if (err != Successful)
2585    {
2586	binfo->status = FS_LFWI_FINISHED;
2587	goto done;
2588    }
2589    binfo->namelen = rep->nameLength;
2590    binfo->remaining = rep->nReplies;
2591
2592    binfo->status = FS_LFWI_REPLY;
2593
2594    /* disable this font server until we've processed this response */
2595    _fs_unmark_block (conn, FS_COMPLETE_REPLY);
2596    conn_stop_listening(conn);
2597done:
2598    _fs_done_read (conn, rep->length << 2);
2599    return err;
2600}
2601
2602/* ARGSUSED */
2603static int
2604fs_start_list_with_info(pointer client, FontPathElementPtr fpe,
2605			const char *pattern, int len, int maxnames, pointer *pdata)
2606{
2607    FSFpePtr		    conn = (FSFpePtr) fpe->private;
2608    FSBlockDataPtr	    blockrec;
2609    FSBlockedListInfoPtr    binfo;
2610    fsListFontsWithXInfoReq req;
2611
2612    if (conn->blockState & FS_GIVE_UP)
2613	return BadFontName;
2614
2615    /* make a new block record, and add it to the end of the list */
2616    blockrec = fs_new_block_rec(fpe, client, FS_LIST_WITH_INFO);
2617    if (!blockrec)
2618	return AllocError;
2619
2620    binfo = (FSBlockedListInfoPtr) blockrec->data;
2621    bzero((char *) binfo, sizeof(FSBlockedListInfoRec));
2622    binfo->status = FS_LFWI_WAITING;
2623
2624    if (conn->blockState & (FS_BROKEN_CONNECTION | FS_RECONNECTING))
2625    {
2626	_fs_pending_reply (conn);
2627	return Suspended;
2628    }
2629
2630    _fs_client_access (conn, client, FALSE);
2631    _fs_client_resolution(conn);
2632
2633    /* send the request */
2634    req.reqType = FS_ListFontsWithXInfo;
2635    req.pad = 0;
2636    req.maxNames = maxnames;
2637    req.nbytes = len;
2638    req.length = (SIZEOF(fsListFontsWithXInfoReq) + len + 3) >> 2;
2639    _fs_add_req_log(conn, FS_ListFontsWithXInfo);
2640    (void) _fs_write(conn, (char *) &req, SIZEOF(fsListFontsWithXInfoReq));
2641    (void) _fs_write_pad(conn, pattern, len);
2642
2643    blockrec->sequenceNumber = conn->current_seq;
2644
2645    _fs_prepare_for_reply (conn);
2646    return Successful;
2647}
2648
2649/* ARGSUSED */
2650static int
2651fs_next_list_with_info(pointer client, FontPathElementPtr fpe,
2652		       char **namep, int *namelenp,
2653		       FontInfoPtr *pFontInfo, int *numFonts,
2654		       pointer private)
2655{
2656    FSFpePtr		    conn = (FSFpePtr) fpe->private;
2657    FSBlockDataPtr	    blockrec;
2658    FSBlockedListInfoPtr    binfo;
2659    int			    err;
2660
2661    /* see if the result is already there */
2662    for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
2663	if (blockrec->type == FS_LIST_WITH_INFO && blockrec->client == client)
2664	    break;
2665
2666    if (!blockrec)
2667    {
2668	/* The only good reason for not finding a blockrec would be if
2669	   disconnect/reconnect to the font server wiped it out and the
2670	   code that called us didn't do the right thing to create
2671	   another one.  Under those circumstances, we need to return an
2672	   error to prevent that code from attempting to interpret the
2673	   information we don't return.  */
2674	return BadFontName;
2675    }
2676
2677    binfo = (FSBlockedListInfoPtr) blockrec->data;
2678
2679    if (binfo->status == FS_LFWI_WAITING)
2680	return Suspended;
2681
2682    *namep = binfo->name;
2683    *namelenp = binfo->namelen;
2684    *pFontInfo = &binfo->info;
2685    *numFonts = binfo->remaining;
2686
2687    /* Restart reply processing from this font server */
2688    conn_start_listening(conn);
2689    if (fs_reply_ready (conn))
2690	_fs_mark_block (conn, FS_COMPLETE_REPLY);
2691
2692    err = blockrec->errcode;
2693    switch (binfo->status) {
2694    case FS_LFWI_FINISHED:
2695	_fs_remove_block_rec(conn, blockrec);
2696	break;
2697    case FS_LFWI_REPLY:
2698	binfo->status = FS_LFWI_WAITING;
2699	blockrec->errcode = StillWorking;
2700	conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
2701	_fs_mark_block (conn, FS_PENDING_REPLY);
2702	break;
2703    }
2704
2705    return err;
2706}
2707
2708/*
2709 * Called when client exits
2710 */
2711
2712static void
2713fs_client_died(pointer client, FontPathElementPtr fpe)
2714{
2715    FSFpePtr	    conn = (FSFpePtr) fpe->private;
2716    FSBlockDataPtr  blockrec,
2717		    depending;
2718    FSClientPtr	    *prev, cur;
2719    fsFreeACReq	    freeac;
2720
2721    for (prev = &conn->clients; (cur = *prev); prev = &cur->next)
2722    {
2723	if (cur->client == client) {
2724	    freeac.reqType = FS_FreeAC;
2725	    freeac.pad = 0;
2726	    freeac.id = cur->acid;
2727	    freeac.length = sizeof (fsFreeACReq) >> 2;
2728	    _fs_add_req_log(conn, FS_FreeAC);
2729	    _fs_write (conn, (char *) &freeac, sizeof (fsFreeACReq));
2730	    *prev = cur->next;
2731	    free (cur);
2732	    break;
2733	}
2734    }
2735    /* find a pending requests */
2736    for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
2737	if (blockrec->client == client)
2738	    break;
2739
2740    if (!blockrec)
2741	return;
2742
2743    /* replace the client pointers in this block rec with the chained one */
2744    if ((depending = blockrec->depending))
2745    {
2746	blockrec->client = depending->client;
2747	blockrec->depending = depending->depending;
2748	blockrec = depending;
2749    }
2750    fs_abort_blockrec(conn, blockrec);
2751}
2752
2753static void
2754_fs_client_access (FSFpePtr conn, pointer client, Bool sync)
2755{
2756    FSClientPtr	*prev,	    cur;
2757    fsCreateACReq	    crac;
2758    fsSetAuthorizationReq   setac;
2759    char		    *authorizations;
2760    int			    authlen;
2761    Bool		    new_cur = FALSE;
2762    char		    padding[4] = { 0, 0, 0, 0 };
2763
2764#ifdef DEBUG
2765    if (conn->blockState & (FS_RECONNECTING|FS_BROKEN_CONNECTION))
2766    {
2767	fprintf (stderr, "Sending requests without a connection\n");
2768    }
2769#endif
2770    for (prev = &conn->clients; (cur = *prev); prev = &cur->next)
2771    {
2772	if (cur->client == client)
2773	{
2774	    if (prev != &conn->clients)
2775	    {
2776		*prev = cur->next;
2777		cur->next = conn->clients;
2778		conn->clients = cur;
2779	    }
2780	    break;
2781	}
2782    }
2783    if (!cur)
2784    {
2785	cur = malloc (sizeof (FSClientRec));
2786	if (!cur)
2787	    return;
2788	cur->client = client;
2789	cur->next = conn->clients;
2790	conn->clients = cur;
2791	cur->acid = GetNewFontClientID ();
2792	new_cur = TRUE;
2793    }
2794    if (new_cur || cur->auth_generation != client_auth_generation(client))
2795    {
2796	if (!new_cur)
2797	{
2798	    fsFreeACReq	freeac;
2799	    freeac.reqType = FS_FreeAC;
2800	    freeac.pad = 0;
2801	    freeac.id = cur->acid;
2802	    freeac.length = sizeof (fsFreeACReq) >> 2;
2803	    _fs_add_req_log(conn, FS_FreeAC);
2804	    _fs_write (conn, (char *) &freeac, sizeof (fsFreeACReq));
2805	}
2806	crac.reqType = FS_CreateAC;
2807	crac.num_auths = set_font_authorizations(&authorizations, &authlen,
2808						 client);
2809	/* Work around bug in xfs versions up through modular release 1.0.8
2810	   which rejects CreateAC packets with num_auths = 0 & authlen < 4 */
2811	if (crac.num_auths == 0) {
2812	    authorizations = padding;
2813	    authlen = 4;
2814	}
2815	crac.length = (sizeof (fsCreateACReq) + authlen + 3) >> 2;
2816	crac.acid = cur->acid;
2817	_fs_add_req_log(conn, FS_CreateAC);
2818	_fs_write(conn, (char *) &crac, sizeof (fsCreateACReq));
2819	_fs_write_pad(conn, authorizations, authlen);
2820	/* ignore reply; we don't even care about it */
2821	conn->curacid = 0;
2822	cur->auth_generation = client_auth_generation(client);
2823    }
2824    if (conn->curacid != cur->acid)
2825    {
2826    	setac.reqType = FS_SetAuthorization;
2827	setac.pad = 0;
2828    	setac.length = sizeof (fsSetAuthorizationReq) >> 2;
2829    	setac.id = cur->acid;
2830    	_fs_add_req_log(conn, FS_SetAuthorization);
2831    	_fs_write(conn, (char *) &setac, sizeof (fsSetAuthorizationReq));
2832	conn->curacid = cur->acid;
2833    }
2834}
2835
2836/*
2837 * Poll a pending connect
2838 */
2839
2840static int
2841_fs_check_connect (FSFpePtr conn)
2842{
2843    int	    ret;
2844
2845    ret = _fs_poll_connect (conn->trans_conn, 0);
2846    switch (ret) {
2847    case FSIO_READY:
2848	conn->fs_fd = _FontTransGetConnectionNumber (conn->trans_conn);
2849	conn_start_listening(conn);
2850	break;
2851    case FSIO_BLOCK:
2852	break;
2853    }
2854    return ret;
2855}
2856
2857/*
2858 * Return an FSIO status while waiting for the completed connection
2859 * reply to arrive
2860 */
2861
2862static fsConnSetup *
2863_fs_get_conn_setup (FSFpePtr conn, int *error, int *setup_len)
2864{
2865    int			ret;
2866    char		*data;
2867    int			headlen;
2868    int			len;
2869    fsConnSetup		*setup;
2870    fsConnSetupAccept	*accept;
2871
2872    ret = _fs_start_read (conn, SIZEOF (fsConnSetup), &data);
2873    if (ret != FSIO_READY)
2874    {
2875	*error = ret;
2876	return 0;
2877    }
2878
2879    setup = (fsConnSetup *) data;
2880    if (setup->major_version > FS_PROTOCOL)
2881    {
2882	*error = FSIO_ERROR;
2883	return 0;
2884    }
2885
2886    headlen = (SIZEOF (fsConnSetup) +
2887	       (setup->alternate_len << 2) +
2888	       (setup->auth_len << 2));
2889    /* On anything but Success, no extra data is sent */
2890    if (setup->status != AuthSuccess)
2891    {
2892	len = headlen;
2893    }
2894    else
2895    {
2896	ret = _fs_start_read (conn, headlen + SIZEOF (fsConnSetupAccept), &data);
2897	if (ret != FSIO_READY)
2898	{
2899	    *error = ret;
2900	    return 0;
2901	}
2902	setup = (fsConnSetup *) data;
2903	accept = (fsConnSetupAccept *) (data + headlen);
2904	len = headlen + (accept->length << 2);
2905    }
2906    ret = _fs_start_read (conn, len, &data);
2907    if (ret != FSIO_READY)
2908    {
2909	*error = ret;
2910	return 0;
2911    }
2912    *setup_len = len;
2913    return (fsConnSetup *) data;
2914}
2915
2916static int
2917_fs_send_conn_client_prefix (FSFpePtr conn)
2918{
2919    fsConnClientPrefix	req;
2920    int			endian;
2921    int			ret;
2922
2923    /* send setup prefix */
2924    endian = 1;
2925    if (*(char *) &endian)
2926	req.byteOrder = 'l';
2927    else
2928	req.byteOrder = 'B';
2929
2930    req.major_version = FS_PROTOCOL;
2931    req.minor_version = FS_PROTOCOL_MINOR;
2932
2933/* XXX add some auth info here */
2934    req.num_auths = 0;
2935    req.auth_len = 0;
2936    ret = _fs_write (conn, (char *) &req, SIZEOF (fsConnClientPrefix));
2937    if (ret != FSIO_READY)
2938	return FSIO_ERROR;
2939    conn->blockedConnectTime = GetTimeInMillis () + FontServerRequestTimeout;
2940    return ret;
2941}
2942
2943static int
2944_fs_recv_conn_setup (FSFpePtr conn)
2945{
2946    int			ret = FSIO_ERROR;
2947    fsConnSetup		*setup;
2948    FSFpeAltPtr		alts;
2949    unsigned int	i, alt_len;
2950    int			setup_len;
2951    char		*alt_save, *alt_names;
2952
2953    setup = _fs_get_conn_setup (conn, &ret, &setup_len);
2954    if (!setup)
2955	return ret;
2956    conn->current_seq = 0;
2957    conn->fsMajorVersion = setup->major_version;
2958    /*
2959     * Create an alternate list from the initial server, but
2960     * don't chain looking for alternates.
2961     */
2962    if (conn->alternate == 0)
2963    {
2964	/*
2965	 * free any existing alternates list, allowing the list to
2966	 * be updated
2967	 */
2968	if (conn->alts)
2969	{
2970	    free (conn->alts);
2971	    conn->alts = 0;
2972	    conn->numAlts = 0;
2973	}
2974	if (setup->num_alternates)
2975	{
2976	    size_t alt_name_len = setup->alternate_len << 2;
2977	    alts = malloc (setup->num_alternates * sizeof (FSFpeAltRec) +
2978			   alt_name_len);
2979	    if (alts)
2980	    {
2981		alt_names = (char *) (setup + 1);
2982		alt_save = (char *) (alts + setup->num_alternates);
2983		for (i = 0; i < setup->num_alternates; i++)
2984		{
2985		    alts[i].subset = alt_names[0];
2986		    alt_len = alt_names[1];
2987		    if (alt_len >= alt_name_len) {
2988			/*
2989			 * Length is longer than setup->alternate_len
2990			 * told us to allocate room for, assume entire
2991			 * alternate list is corrupted.
2992			 */
2993#ifdef DEBUG
2994			fprintf (stderr,
2995				 "invalid alt list (length %lx >= %lx)\n",
2996				 (long) alt_len, (long) alt_name_len);
2997#endif
2998			free(alts);
2999			return FSIO_ERROR;
3000		    }
3001		    alts[i].name = alt_save;
3002		    memcpy (alt_save, alt_names + 2, alt_len);
3003		    alt_save[alt_len] = '\0';
3004		    alt_save += alt_len + 1;
3005		    alt_name_len -= alt_len + 1;
3006		    alt_names += _fs_pad_length (alt_len + 2);
3007		}
3008		conn->numAlts = setup->num_alternates;
3009		conn->alts = alts;
3010	    }
3011	}
3012    }
3013    _fs_done_read (conn, setup_len);
3014    if (setup->status != AuthSuccess)
3015	return FSIO_ERROR;
3016    return FSIO_READY;
3017}
3018
3019static int
3020_fs_open_server (FSFpePtr conn)
3021{
3022    int	    ret;
3023    char    *servername;
3024
3025    if (conn->alternate == 0)
3026	servername = conn->servername;
3027    else
3028	servername = conn->alts[conn->alternate-1].name;
3029    conn->trans_conn = _fs_connect (servername, &ret);
3030    conn->blockedConnectTime = GetTimeInMillis () + FS_RECONNECT_WAIT;
3031    return ret;
3032}
3033
3034static char *
3035_fs_catalog_name (char *servername)
3036{
3037    char    *sp;
3038
3039    sp = strchr (servername, '/');
3040    if (!sp)
3041	return 0;
3042    return strrchr (sp + 1, '/');
3043}
3044
3045static int
3046_fs_send_init_packets (FSFpePtr conn)
3047{
3048    fsSetResolutionReq	    srreq;
3049    fsSetCataloguesReq	    screq;
3050    int			    num_cats,
3051			    clen;
3052    char		    *catalogues;
3053    char		    *cat;
3054    char		    len;
3055    char		    *end;
3056    int			    num_res;
3057    FontResolutionPtr	    res;
3058
3059#define	CATALOGUE_SEP	'+'
3060
3061    res = GetClientResolutions(&num_res);
3062    if (num_res)
3063    {
3064	srreq.reqType = FS_SetResolution;
3065	srreq.num_resolutions = num_res;
3066	srreq.length = (SIZEOF(fsSetResolutionReq) +
3067			(num_res * SIZEOF(fsResolution)) + 3) >> 2;
3068
3069	_fs_add_req_log(conn, FS_SetResolution);
3070	if (_fs_write(conn, (char *) &srreq, SIZEOF(fsSetResolutionReq)) != FSIO_READY)
3071	    return FSIO_ERROR;
3072	if (_fs_write_pad(conn, (char *) res, (num_res * SIZEOF(fsResolution))) != FSIO_READY)
3073	    return FSIO_ERROR;
3074    }
3075
3076    catalogues = 0;
3077    if (conn->alternate != 0)
3078	catalogues = _fs_catalog_name (conn->alts[conn->alternate-1].name);
3079    if (!catalogues)
3080	catalogues = _fs_catalog_name (conn->servername);
3081
3082    if (!catalogues)
3083    {
3084	conn->has_catalogues = FALSE;
3085	return FSIO_READY;
3086    }
3087    conn->has_catalogues = TRUE;
3088
3089    /* turn cats into counted list */
3090    catalogues++;
3091
3092    cat = catalogues;
3093    num_cats = 0;
3094    clen = 0;
3095    while (*cat)
3096    {
3097	num_cats++;
3098	end = strchr(cat, CATALOGUE_SEP);
3099	if (!end)
3100	    end = cat + strlen (cat);
3101	clen += (end - cat) + 1;	/* length byte + string */
3102	cat = end;
3103    }
3104
3105    screq.reqType = FS_SetCatalogues;
3106    screq.num_catalogues = num_cats;
3107    screq.length = (SIZEOF(fsSetCataloguesReq) + clen + 3) >> 2;
3108
3109    _fs_add_req_log(conn, FS_SetCatalogues);
3110    if (_fs_write(conn, (char *) &screq, SIZEOF(fsSetCataloguesReq)) != FSIO_READY)
3111	return FSIO_ERROR;
3112
3113    while (*cat)
3114    {
3115	num_cats++;
3116	end = strchr(cat, CATALOGUE_SEP);
3117	if (!end)
3118	    end = cat + strlen (cat);
3119	len = end - cat;
3120	if (_fs_write (conn, &len, 1) != FSIO_READY)
3121	    return FSIO_ERROR;
3122	if (_fs_write (conn, cat, (int) len) != FSIO_READY)
3123	    return FSIO_ERROR;
3124	cat = end;
3125    }
3126
3127    if (_fs_write (conn, "....", _fs_pad_length (clen) - clen) != FSIO_READY)
3128	return FSIO_ERROR;
3129
3130    return FSIO_READY;
3131}
3132
3133static int
3134_fs_send_cat_sync (FSFpePtr conn)
3135{
3136    fsListCataloguesReq	    lcreq;
3137
3138    /*
3139     * now sync up with the font server, to see if an error was generated
3140     * by a bogus catalogue
3141     */
3142    lcreq.reqType = FS_ListCatalogues;
3143    lcreq.data = 0;
3144    lcreq.length = (SIZEOF(fsListCataloguesReq)) >> 2;
3145    lcreq.maxNames = 0;
3146    lcreq.nbytes = 0;
3147    lcreq.pad2 = 0;
3148    _fs_add_req_log(conn, FS_SetCatalogues);
3149    if (_fs_write(conn, (char *) &lcreq, SIZEOF(fsListCataloguesReq)) != FSIO_READY)
3150	return FSIO_ERROR;
3151    conn->blockedConnectTime = GetTimeInMillis () + FontServerRequestTimeout;
3152    return FSIO_READY;
3153}
3154
3155static int
3156_fs_recv_cat_sync (FSFpePtr conn)
3157{
3158    fsGenericReply  *reply;
3159    fsError	    *error;
3160    int		    err;
3161    int		    ret;
3162
3163    reply = fs_get_reply (conn, &err);
3164    if (!reply)
3165	return err;
3166
3167    ret = FSIO_READY;
3168    if (reply->type == FS_Error)
3169    {
3170	error = (fsError *) reply;
3171	if (error->major_opcode == FS_SetCatalogues)
3172	    ret = FSIO_ERROR;
3173    }
3174    _fs_done_read (conn, reply->length << 2);
3175    return ret;
3176}
3177
3178static void
3179_fs_close_server (FSFpePtr conn)
3180{
3181    _fs_unmark_block (conn, FS_PENDING_WRITE|FS_BROKEN_WRITE|FS_COMPLETE_REPLY|FS_BROKEN_CONNECTION);
3182    if (conn->trans_conn)
3183    {
3184	_FontTransClose (conn->trans_conn);
3185	conn->trans_conn = 0;
3186	_fs_io_reinit (conn);
3187    }
3188    if (conn->fs_fd >= 0)
3189    {
3190	conn_stop_listening(conn);
3191	conn->fs_fd = -1;
3192    }
3193    conn->fs_conn_state = FS_CONN_UNCONNECTED;
3194}
3195
3196static int
3197_fs_do_setup_connection (FSFpePtr conn)
3198{
3199    int	    ret;
3200
3201    do
3202    {
3203#ifdef DEBUG
3204	fprintf (stderr, "fs_do_setup_connection state %d\n", conn->fs_conn_state);
3205#endif
3206	switch (conn->fs_conn_state) {
3207	case FS_CONN_UNCONNECTED:
3208	    ret = _fs_open_server (conn);
3209	    if (ret == FSIO_BLOCK)
3210		conn->fs_conn_state = FS_CONN_CONNECTING;
3211	    break;
3212	case FS_CONN_CONNECTING:
3213	    ret = _fs_check_connect (conn);
3214	    break;
3215	case FS_CONN_CONNECTED:
3216	    ret = _fs_send_conn_client_prefix (conn);
3217	    break;
3218	case FS_CONN_SENT_PREFIX:
3219	    ret = _fs_recv_conn_setup (conn);
3220	    break;
3221	case FS_CONN_RECV_INIT:
3222	    ret = _fs_send_init_packets (conn);
3223	    if (conn->has_catalogues)
3224		ret = _fs_send_cat_sync (conn);
3225	    break;
3226	case FS_CONN_SENT_CAT:
3227	    if (conn->has_catalogues)
3228		ret = _fs_recv_cat_sync (conn);
3229	    else
3230		ret = FSIO_READY;
3231	    break;
3232	default:
3233	    ret = FSIO_READY;
3234	    break;
3235	}
3236	switch (ret) {
3237	case FSIO_READY:
3238	    if (conn->fs_conn_state < FS_CONN_RUNNING)
3239		conn->fs_conn_state++;
3240	    break;
3241	case FSIO_BLOCK:
3242	    if (TimeCmp (GetTimeInMillis (), <, conn->blockedConnectTime))
3243		break;
3244	    ret = FSIO_ERROR;
3245	    /* fall through... */
3246	case FSIO_ERROR:
3247	    _fs_close_server (conn);
3248	    /*
3249	     * Try the next alternate
3250	     */
3251	    if (conn->alternate < conn->numAlts)
3252	    {
3253		conn->alternate++;
3254		ret = FSIO_READY;
3255	    }
3256	    else
3257		conn->alternate = 0;
3258	    break;
3259	}
3260    } while (conn->fs_conn_state != FS_CONN_RUNNING && ret == FSIO_READY);
3261    if (ret == FSIO_READY)
3262	conn->generation = ++generationCount;
3263    return ret;
3264}
3265
3266static int
3267_fs_wait_connect (FSFpePtr conn)
3268{
3269    int	    ret;
3270
3271    for (;;)
3272    {
3273	ret = _fs_do_setup_connection (conn);
3274	if (ret != FSIO_BLOCK)
3275	    break;
3276	if (conn->fs_conn_state <= FS_CONN_CONNECTING)
3277	    ret = _fs_poll_connect (conn->trans_conn, 1000);
3278	else
3279	    ret = _fs_wait_for_readable (conn, 1000);
3280	if (ret == FSIO_ERROR)
3281	    break;
3282    }
3283    return ret;
3284}
3285
3286/*
3287 * Poll a connection in the process of reconnecting
3288 */
3289static void
3290_fs_check_reconnect (FSFpePtr conn)
3291{
3292    int	    ret;
3293
3294    ret = _fs_do_setup_connection (conn);
3295    switch (ret) {
3296    case FSIO_READY:
3297	_fs_unmark_block (conn, FS_RECONNECTING|FS_GIVE_UP);
3298	_fs_restart_connection (conn);
3299	break;
3300    case FSIO_BLOCK:
3301	break;
3302    case FSIO_ERROR:
3303	conn->brokenConnectionTime = GetTimeInMillis () + FS_RECONNECT_POLL;
3304	break;
3305    }
3306}
3307
3308/*
3309 * Start the reconnection process
3310 */
3311static void
3312_fs_start_reconnect (FSFpePtr conn)
3313{
3314    if (conn->blockState & FS_RECONNECTING)
3315	return;
3316    conn->alternate = 0;
3317    _fs_mark_block (conn, FS_RECONNECTING);
3318    _fs_unmark_block (conn, FS_BROKEN_CONNECTION);
3319    _fs_check_reconnect (conn);
3320}
3321
3322
3323static FSFpePtr
3324_fs_init_conn (const char *servername, FontPathElementPtr fpe)
3325{
3326    FSFpePtr	conn;
3327    size_t	snlen = strlen (servername) + 1;
3328
3329    conn = calloc (1, sizeof (FSFpeRec) + snlen);
3330    if (!conn)
3331	return 0;
3332    if (!_fs_io_init (conn))
3333    {
3334	free (conn);
3335	return 0;
3336    }
3337    conn->servername = (char *) (conn + 1);
3338    conn->fs_conn_state = FS_CONN_UNCONNECTED;
3339    conn->fs_fd = -1;
3340    conn->fpe = fpe;
3341    strlcpy (conn->servername, servername, snlen);
3342    return conn;
3343}
3344
3345static void
3346_fs_free_conn (FSFpePtr conn)
3347{
3348    _fs_close_server (conn);
3349    _fs_io_fini (conn);
3350    if (conn->alts)
3351	free (conn->alts);
3352    free (conn);
3353}
3354
3355/*
3356 * called at server init time
3357 */
3358
3359static const xfont2_fpe_funcs_rec fs_fpe_funcs = {
3360	.version = XFONT2_FPE_FUNCS_VERSION,
3361	.name_check = fs_name_check,
3362	.init_fpe = fs_init_fpe,
3363	.free_fpe = fs_free_fpe,
3364	.reset_fpe = fs_reset_fpe,
3365	.open_font = fs_open_font,
3366	.close_font = fs_close_font,
3367	.list_fonts = fs_list_fonts,
3368	.start_list_fonts_with_info = fs_start_list_with_info,
3369	.list_next_font_with_info = fs_next_list_with_info,
3370	.wakeup_fpe = fs_wakeup,
3371	.client_died = fs_client_died,
3372	.load_glyphs = _fs_load_glyphs,
3373	.start_list_fonts_and_aliases = (StartLaFunc) 0,
3374	.list_next_font_or_alias = (NextLaFunc) 0,
3375	.set_path_hook = (SetPathFunc) 0
3376};
3377
3378void
3379fs_register_fpe_functions(void)
3380{
3381    register_fpe_funcs(&fs_fpe_funcs);
3382}
3383