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