fserve.c revision 60da515c
1/*
2
3Copyright 1990, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25*/
26
27/*
28 * Copyright 1990 Network Computing Devices
29 *
30 * Permission to use, copy, modify, distribute, and sell this software and
31 * its documentation for any purpose is hereby granted without fee, provided
32 * that the above copyright notice appear in all copies and that both that
33 * copyright notice and this permission notice appear in supporting
34 * documentation, and that the names of Network Computing Devices, or Digital
35 * not be used in advertising or publicity pertaining to distribution
36 * of the software without specific, written prior permission.
37 *
38 * NETWORK COMPUTING DEVICES, AND DIGITAL AND DISCLAIM ALL WARRANTIES WITH
39 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
40 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES,
41 * OR DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
42 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
43 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
44 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
45 * THIS SOFTWARE.
46 *
47 * Author:  	Dave Lemke, Network Computing Devices, Inc
48 */
49/*
50 * font server specific font access
51 */
52
53#ifdef HAVE_CONFIG_H
54#include <config.h>
55#endif
56#include "libxfontint.h"
57#include "src/util/replace.h"
58
59#ifdef WIN32
60#define _WILLWINSOCK_
61#endif
62#define FONT_t
63#define TRANS_CLIENT
64#include	"X11/Xtrans/Xtrans.h"
65#include	"X11/Xpoll.h"
66#include	<X11/fonts/FS.h>
67#include	<X11/fonts/FSproto.h>
68#include	<X11/X.h>
69#include	<X11/Xos.h>
70#include	<X11/fonts/fontmisc.h>
71#include	<X11/fonts/fontstruct.h>
72#include	"fservestr.h"
73#include	<X11/fonts/fontutil.h>
74#include	<errno.h>
75#include	<limits.h>
76
77#include	<time.h>
78#define Time_t time_t
79
80#ifdef NCD
81#include	<ncd/nvram.h>
82#endif
83
84#include <stddef.h>
85
86#ifndef MIN
87#define MIN(a,b)    ((a)<(b)?(a):(b))
88#endif
89#define TimeCmp(a,c,b)	((int) ((a) - (b)) c 0)
90
91#define NONZEROMETRICS(pci) ((pci)->leftSideBearing || \
92			     (pci)->rightSideBearing || \
93			     (pci)->ascent || \
94			     (pci)->descent || \
95			     (pci)->characterWidth)
96
97/*
98 * SIZEOF(r) is in bytes, length fields in the protocol are in 32-bit words,
99 * so this converts for doing size comparisons.
100 */
101#define LENGTHOF(r)	(SIZEOF(r) >> 2)
102
103/* Somewhat arbitrary limit on maximum reply size we'll try to read. */
104#define MAX_REPLY_LENGTH	((64 * 1024 * 1024) >> 2)
105
106static int fs_read_glyphs ( FontPathElementPtr fpe, FSBlockDataPtr blockrec );
107static int fs_read_list ( FontPathElementPtr fpe, FSBlockDataPtr blockrec );
108static int fs_read_list_info ( FontPathElementPtr fpe,
109			       FSBlockDataPtr blockrec );
110
111static void fs_block_handler ( void *wt );
112
113static int fs_wakeup ( FontPathElementPtr fpe );
114
115/*
116 * List of all FPEs
117 */
118static FSFpePtr fs_fpes;
119/*
120 * Union of all FPE blockStates
121 */
122static CARD32	fs_blockState;
123
124static int _fs_restart_connection ( FSFpePtr conn );
125static void fs_send_query_bitmaps ( FontPathElementPtr fpe,
126				   FSBlockDataPtr blockrec );
127static int fs_send_close_font (FSFpePtr conn, Font id);
128static void fs_client_died ( pointer client, FontPathElementPtr fpe );
129static void _fs_client_access ( FSFpePtr conn, pointer client, Bool sync );
130static void _fs_client_resolution ( FSFpePtr conn );
131static fsGenericReply *fs_get_reply (FSFpePtr conn, int *error);
132static int fs_await_reply (FSFpePtr conn);
133static void _fs_do_blocked (FSFpePtr conn);
134static void fs_cleanup_bfont (FSFpePtr conn, FSBlockedFontPtr bfont);
135
136char _fs_glyph_undefined;
137char _fs_glyph_requested;
138static char _fs_glyph_zero_length;
139
140static int  generationCount;
141
142static int FontServerRequestTimeout = 30 * 1000;
143
144static void
145_fs_close_server (FSFpePtr conn);
146
147static FSFpePtr
148_fs_init_conn (const char *servername, FontPathElementPtr fpe);
149
150static int
151_fs_wait_connect (FSFpePtr conn);
152
153static int
154_fs_send_init_packets (FSFpePtr conn);
155
156static void
157_fs_check_reconnect (FSFpePtr conn);
158
159static void
160_fs_start_reconnect (FSFpePtr conn);
161
162static void
163_fs_free_conn (FSFpePtr conn);
164
165static int
166fs_free_fpe(FontPathElementPtr fpe);
167
168static void
169fs_fd_handler(int fd, void *data);
170
171/*
172 * Font server access
173 *
174 * the basic idea for the non-blocking access is to have the function
175 * called multiple times until the actual data is returned, instead
176 * of ClientBlocked.
177 *
178 * the first call to the function will cause the request to be sent to
179 * the font server, and a block record to be stored in the fpe's list
180 * of outstanding requests.  the FS block handler also sticks the
181 * proper set of fd's into the select mask.  when data is ready to be
182 * read in, the FS wakup handler will be hit.  this will read the
183 * data off the wire into the proper block record, and then signal the
184 * client that caused the block so that it can restart.  it will then
185 * call the access function again, which will realize that the data has
186 * arrived and return it.
187 */
188
189
190#ifdef DEBUG
191static void
192_fs_add_req_log(FSFpePtr conn, int opcode)
193{
194    conn->current_seq++;
195    fprintf (stderr, "\t\tRequest: %5d Opcode: %2d\n",
196	     conn->current_seq, opcode);
197    conn->reqbuffer[conn->reqindex].opcode = opcode;
198    conn->reqbuffer[conn->reqindex].sequence = conn->current_seq;
199    conn->reqindex++;
200    if (conn->reqindex == REQUEST_LOG_SIZE)
201	conn->reqindex = 0;
202}
203
204static void
205_fs_add_rep_log (FSFpePtr conn, fsGenericReply *rep)
206{
207    int	    i;
208
209    for (i = 0; i < REQUEST_LOG_SIZE; i++)
210	if (conn->reqbuffer[i].sequence == rep->sequenceNumber)
211	    break;
212    if (i == REQUEST_LOG_SIZE)
213	fprintf (stderr, "\t\t\t\t\tReply:  %5d Opcode: unknown\n",
214		 rep->sequenceNumber);
215    else
216	fprintf (stderr, "\t\t\t\t\tReply:  %5d Opcode: %d\n",
217		 rep->sequenceNumber,
218		 conn->reqbuffer[i].opcode);
219}
220
221#define _fs_reply_failed(rep, name, op) do {                            \
222    if (rep) {                                                          \
223        if (rep->type == FS_Error)                                      \
224            fprintf (stderr, "Error: %d Request: %s\n",                 \
225                     ((fsError *)rep)->request, #name);                 \
226        else                                                            \
227            fprintf (stderr, "Bad Length for %s Reply: %u %s %d\n",     \
228                     #name, (unsigned) rep->length, op, LENGTHOF(name));\
229    }                                                                   \
230} while (0)
231
232#else
233#define _fs_add_req_log(conn,op)    ((conn)->current_seq++)
234#define _fs_add_rep_log(conn,rep)
235#define _fs_reply_failed(rep,name,op)
236#endif
237
238static Bool
239fs_name_check(const char *name)
240{
241    /* Just make sure there is a protocol/ prefix */
242    return (name && *name != '/' && strchr(name, '/'));
243}
244
245static void
246_fs_client_resolution(FSFpePtr conn)
247{
248    fsSetResolutionReq srreq;
249    int         num_res;
250    FontResolutionPtr res;
251
252    res = GetClientResolutions(&num_res);
253
254    if (num_res) {
255	srreq.reqType = FS_SetResolution;
256	srreq.num_resolutions = num_res;
257	srreq.length = (SIZEOF(fsSetResolutionReq) +
258			(num_res * SIZEOF(fsResolution)) + 3) >> 2;
259
260	_fs_add_req_log(conn, FS_SetResolution);
261	if (_fs_write(conn, (char *) &srreq, SIZEOF(fsSetResolutionReq)) != -1)
262	    (void)_fs_write_pad(conn, (char *) res,
263				(num_res * SIZEOF(fsResolution)));
264    }
265}
266
267/*
268 * close font server and remove any state associated with
269 * this connection - this includes any client records.
270 */
271
272static void
273fs_close_conn(FSFpePtr conn)
274{
275    FSClientPtr	client, nclient;
276
277    _fs_close_server (conn);
278
279    for (client = conn->clients; client; client = nclient)
280    {
281	nclient = client->next;
282	free (client);
283    }
284    conn->clients = NULL;
285}
286
287/*
288 * the wakeup handlers have to be set when the FPE is open, and not
289 * removed until it is freed, in order to handle unexpected data, like
290 * events
291 */
292/* ARGSUSED */
293static int
294fs_init_fpe(FontPathElementPtr fpe)
295{
296    FSFpePtr    conn;
297    const char  *name;
298    int         err;
299    int		ret;
300
301    /* open font server */
302    /* create FS specific fpe info */
303    name = fpe->name;
304
305    /* hack for old style names */
306    if (*name == ':')
307	name++;			/* skip ':' */
308
309    conn = _fs_init_conn (name, fpe);
310    if (!conn)
311	err = AllocError;
312    else
313    {
314	err = init_fs_handlers2 (fpe, fs_block_handler);
315	if (err != Successful)
316	{
317	    _fs_free_conn (conn);
318	    err = AllocError;
319	}
320	else
321	{
322	    fpe->private = conn;
323	    conn->next = fs_fpes;
324	    fs_fpes = conn;
325	    ret = _fs_wait_connect (conn);
326	    if (ret != FSIO_READY)
327	    {
328		fs_free_fpe (fpe);
329		err = BadFontPath;
330	    }
331	    else
332		err = Successful;
333	}
334    }
335
336    if (err == Successful)
337    {
338#ifdef NCD
339	if (configData.ExtendedFontDiags)
340	    printf("Connected to font server \"%s\"\n", name);
341#endif
342#ifdef DEBUG
343	fprintf (stderr, "connected to FS \"%s\"\n", name);
344#endif
345    }
346    else
347    {
348#ifdef DEBUG
349	fprintf(stderr, "failed to connect to FS \"%s\" %d\n", name, err);
350#endif
351#ifdef NCD
352	if (configData.ExtendedFontDiags)
353	    printf("Failed to connect to font server \"%s\"\n", name);
354#endif
355	;
356    }
357    return err;
358}
359
360static int
361fs_reset_fpe(FontPathElementPtr fpe)
362{
363    (void) _fs_send_init_packets((FSFpePtr) fpe->private);
364    return Successful;
365}
366
367/*
368 * this shouldn't be called till all refs to the FPE are gone
369 */
370
371static int
372fs_free_fpe(FontPathElementPtr fpe)
373{
374    FSFpePtr    conn = (FSFpePtr) fpe->private, *prev;
375
376    /* unhook from chain of all font servers */
377    for (prev = &fs_fpes; *prev; prev = &(*prev)->next)
378    {
379	if (*prev == conn)
380	{
381	    *prev = conn->next;
382	    break;
383	}
384    }
385    _fs_unmark_block (conn, conn->blockState);
386    fs_close_conn(conn);
387    remove_fs_handlers2(fpe, fs_block_handler, fs_fpes == 0);
388    _fs_free_conn (conn);
389    fpe->private = (pointer) 0;
390
391#ifdef NCD
392    if (configData.ExtendedFontDiags)
393	printf("Disconnected from font server \"%s\"\n", fpe->name);
394#endif
395#ifdef DEBUG
396    fprintf (stderr, "disconnect from FS \"%s\"\n", fpe->name);
397#endif
398
399    return Successful;
400}
401
402static      FSBlockDataPtr
403fs_new_block_rec(FontPathElementPtr fpe, pointer client, int type)
404{
405    FSBlockDataPtr blockrec,
406                *prev;
407    FSFpePtr    conn = (FSFpePtr) fpe->private;
408    int         size;
409
410    switch (type) {
411    case FS_OPEN_FONT:
412	size = sizeof(FSBlockedFontRec);
413	break;
414    case FS_LOAD_GLYPHS:
415	size = sizeof(FSBlockedGlyphRec);
416	break;
417    case FS_LIST_FONTS:
418	size = sizeof(FSBlockedListRec);
419	break;
420    case FS_LIST_WITH_INFO:
421	size = sizeof(FSBlockedListInfoRec);
422	break;
423    default:
424	size = 0;
425	break;
426    }
427    blockrec = malloc(sizeof(FSBlockDataRec) + size);
428    if (!blockrec)
429	return (FSBlockDataPtr) 0;
430    blockrec->data = (pointer) (blockrec + 1);
431    blockrec->client = client;
432    blockrec->sequenceNumber = -1;
433    blockrec->errcode = StillWorking;
434    blockrec->type = type;
435    blockrec->depending = 0;
436    blockrec->next = (FSBlockDataPtr) 0;
437
438    /* stick it on the end of the list (since its expected last) */
439    for (prev = &conn->blockedRequests; *prev; prev = &(*prev)->next)
440	;
441    *prev = blockrec;
442
443    return blockrec;
444}
445
446static void
447_fs_set_pending_reply (FSFpePtr conn)
448{
449    FSBlockDataPtr  blockrec;
450
451    for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
452	if (blockrec->errcode == StillWorking)
453	    break;
454    if (blockrec)
455    {
456	conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
457	_fs_mark_block (conn, FS_PENDING_REPLY);
458    }
459    else
460	_fs_unmark_block (conn, FS_PENDING_REPLY);
461}
462
463static void
464_fs_remove_block_rec(FSFpePtr conn, FSBlockDataPtr blockrec)
465{
466    FSBlockDataPtr *prev;
467
468    for (prev = &conn->blockedRequests; *prev; prev = &(*prev)->next)
469	if (*prev == blockrec)
470	{
471	    *prev = blockrec->next;
472	    break;
473	}
474    if (blockrec->type == FS_LOAD_GLYPHS)
475    {
476	FSBlockedGlyphPtr bglyph = (FSBlockedGlyphPtr)blockrec->data;
477	if (bglyph->num_expected_ranges)
478	    free(bglyph->expected_ranges);
479    }
480    free(blockrec);
481    _fs_set_pending_reply (conn);
482}
483
484static void
485_fs_signal_clients_depending(FSClientsDependingPtr *clients_depending)
486{
487    FSClientsDependingPtr p;
488
489    while ((p = *clients_depending))
490    {
491	*clients_depending = p->next;
492	ClientSignal(p->client);
493	free(p);
494    }
495}
496
497static int
498_fs_add_clients_depending(FSClientsDependingPtr *clients_depending, pointer client)
499{
500    FSClientsDependingPtr   new, cd;
501
502    for (; (cd = *clients_depending);
503	 clients_depending = &(*clients_depending)->next)
504    {
505	if (cd->client == client)
506	    return Suspended;
507    }
508
509    new = malloc (sizeof (FSClientsDependingRec));
510    if (!new)
511	return BadAlloc;
512
513    new->client = client;
514    new->next = 0;
515    *clients_depending = new;
516    return Suspended;
517}
518
519static void
520conn_start_listening(FSFpePtr conn)
521{
522    if (!conn->fs_listening) {
523	add_fs_fd(conn->fs_fd, fs_fd_handler, conn->fpe);
524	conn->fs_listening = TRUE;
525    }
526}
527
528static void
529conn_stop_listening(FSFpePtr conn)
530{
531    if (conn->fs_listening) {
532	remove_fs_fd(conn->fs_fd);
533	conn->fs_listening = FALSE;
534    }
535}
536
537/*
538 * When a request is aborted due to a font server failure,
539 * signal any depending clients to restart their dependant
540 * requests
541 */
542static void
543_fs_clean_aborted_blockrec(FSFpePtr conn, FSBlockDataPtr blockrec)
544{
545    switch(blockrec->type) {
546    case FS_OPEN_FONT: {
547	FSBlockedFontPtr bfont = (FSBlockedFontPtr)blockrec->data;
548
549	fs_cleanup_bfont (conn, bfont);
550	_fs_signal_clients_depending(&bfont->clients_depending);
551	break;
552    }
553    case FS_LOAD_GLYPHS: {
554	FSBlockedGlyphPtr bglyph = (FSBlockedGlyphPtr)blockrec->data;
555
556	_fs_clean_aborted_loadglyphs(bglyph->pfont,
557				     bglyph->num_expected_ranges,
558				     bglyph->expected_ranges);
559	_fs_signal_clients_depending(&bglyph->clients_depending);
560	break;
561    }
562    case FS_LIST_FONTS:
563	break;
564    case FS_LIST_WITH_INFO: {
565	FSBlockedListInfoPtr binfo;
566	binfo = (FSBlockedListInfoPtr) blockrec->data;
567	if (binfo->status == FS_LFWI_REPLY)
568	    conn_start_listening(conn);
569	_fs_free_props (&binfo->info);
570    }
571    default:
572	break;
573    }
574}
575
576static void
577fs_abort_blockrec(FSFpePtr conn, FSBlockDataPtr blockrec)
578{
579    _fs_clean_aborted_blockrec (conn, blockrec);
580    _fs_remove_block_rec (conn, blockrec);
581}
582
583/*
584 * Tell the font server we've failed to complete an open and
585 * then unload the partially created font
586 */
587static void
588fs_cleanup_bfont (FSFpePtr conn, FSBlockedFontPtr bfont)
589{
590    if (bfont->pfont)
591    {
592	/* make sure the FS knows we choked on it */
593	fs_send_close_font(conn, bfont->fontid);
594
595	/*
596	 * Either unload the font if it's being opened for
597	 * the first time, or smash the generation field to
598	 * mark this font as an orphan
599	 */
600	if (!(bfont->flags & FontReopen))
601	{
602	    if (bfont->freeFont)
603		(*bfont->pfont->unload_font) (bfont->pfont);
604#ifdef DEBUG
605	    else
606		fprintf (stderr, "Not freeing other font in cleanup_bfont\n");
607#endif
608	    bfont->pfont = 0;
609	}
610	else
611	{
612	    FSFontDataRec *fsd = (FSFontDataRec *)bfont->pfont->fpePrivate;
613	    fsd->generation = -1;
614	}
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 %ld > MAX_REPLY_LENGTH, disconnecting"
653	       " from font server\n", (long)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 (conn, 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 (conn, 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 (conn, 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) < %u\n",
937		bufleft, (unsigned) 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 (%u)\n",
951		bufleft, (unsigned) 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 (conn, 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 (conn, 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 (conn, 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) > (%u - %d) / %d\n",
1085		numExtents, (unsigned) rep->length,
1086		LENGTHOF(fsQueryXExtents16Reply), LENGTHOF(fsXCharInfo));
1087#endif
1088	pCI = NULL;
1089    }
1090    else
1091	pCI = mallocarray(numInfos, sizeof(CharInfoRec));
1092
1093    if (!pCI)
1094    {
1095	_fs_done_read (conn, rep->length << 2);
1096	fs_cleanup_bfont(conn, 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    fsd->generation = conn->generation;
1700
1701    bfont = (FSBlockedFontPtr) blockrec->data;
1702    bfont->fontid = fsd->fontid;
1703    bfont->pfont = font;
1704    bfont->state = FS_OPEN_REPLY;
1705    bfont->flags = flags;
1706    bfont->format = fsd->format;
1707    bfont->clients_depending = (FSClientsDependingPtr)0;
1708    bfont->freeFont = (flags & FontReopen) == 0;
1709
1710    /*
1711     * Must check this before generating any protocol, otherwise we'll
1712     * mess up a reconnect in progress
1713     */
1714    if (conn->blockState & (FS_BROKEN_CONNECTION | FS_RECONNECTING))
1715    {
1716	_fs_pending_reply (conn);
1717	return Suspended;
1718    }
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 (conn, 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 (conn, 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(FSFpePtr conn, Font id)
1859{
1860    fsCloseReq  req;
1861
1862    if (conn->blockState & FS_GIVE_UP)
1863	return Successful;
1864    /* tell the font server to close the font */
1865    req.reqType = FS_CloseFont;
1866    req.pad = 0;
1867    req.length = SIZEOF(fsCloseReq) >> 2;
1868    req.id = id;
1869    _fs_add_req_log(conn, FS_CloseFont);
1870    _fs_write(conn, (char *) &req, SIZEOF(fsCloseReq));
1871
1872    return Successful;
1873}
1874
1875/* ARGSUSED */
1876static void
1877fs_close_font(FontPathElementPtr fpe, FontPtr pfont)
1878{
1879    FSFontDataPtr   fsd = (FSFontDataPtr) pfont->fpePrivate;
1880    FSFpePtr	    conn = (FSFpePtr) fpe->private;
1881
1882    if (conn->generation == fsd->generation)
1883	fs_send_close_font(conn, fsd->fontid);
1884
1885#ifdef DEBUG
1886    {
1887	FSBlockDataPtr	    blockrec;
1888	FSBlockedFontPtr    bfont;
1889
1890	for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
1891	{
1892	    if (blockrec->type == FS_OPEN_FONT)
1893	    {
1894		bfont = (FSBlockedFontPtr) blockrec->data;
1895		if (bfont->pfont == pfont)
1896		    fprintf (stderr, "closing font which hasn't been opened\n");
1897	    }
1898	}
1899    }
1900#endif
1901    (*pfont->unload_font) (pfont);
1902}
1903
1904static int
1905fs_read_glyphs(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
1906{
1907    FSBlockedGlyphPtr	    bglyph = (FSBlockedGlyphPtr) blockrec->data;
1908    FSBlockedFontPtr	    bfont = (FSBlockedFontPtr) blockrec->data;
1909    FSFpePtr		    conn = (FSFpePtr) fpe->private;
1910    FontPtr		    pfont = bglyph->pfont;
1911					/* works for either blocked font
1912					   or glyph rec...  pfont is at
1913					   the very beginning of both
1914					   blockrec->data structures */
1915    FSFontDataPtr	    fsd = (FSFontDataPtr) (pfont->fpePrivate);
1916    FSFontPtr		    fsdata = (FSFontPtr) pfont->fontPrivate;
1917    FontInfoPtr		    pfi = &pfont->info;
1918    fsQueryXBitmaps16Reply  *rep;
1919    char		    *buf;
1920    long		    bufleft; /* length of reply left to use */
1921    fsOffset32		    *ppbits;
1922    fsOffset32		    local_off;
1923    char		    *off_adr;
1924    pointer		    pbitmaps;
1925    char		    *bits, *allbits;
1926#ifdef DEBUG
1927    char		    *origallbits;
1928#endif
1929    int			    i,
1930			    err;
1931    int			    nranges = 0;
1932    int			    ret;
1933    fsRange		    *nextrange = 0;
1934    unsigned long	    minchar, maxchar;
1935
1936    rep = (fsQueryXBitmaps16Reply *) fs_get_reply (conn, &ret);
1937    if (!rep || rep->type == FS_Error ||
1938	(rep->length < LENGTHOF(fsQueryXBitmaps16Reply)))
1939    {
1940	if (ret == FSIO_BLOCK)
1941	    return StillWorking;
1942	if (rep)
1943	    _fs_done_read (conn, rep->length << 2);
1944	err = AllocError;
1945	_fs_reply_failed (rep, fsQueryXBitmaps16Reply, "<");
1946	goto bail;
1947    }
1948
1949    buf = (char *) rep;
1950    buf += SIZEOF (fsQueryXBitmaps16Reply);
1951
1952    bufleft = rep->length << 2;
1953    bufleft -= SIZEOF (fsQueryXBitmaps16Reply);
1954
1955    if ((bufleft / SIZEOF (fsOffset32)) < rep->num_chars)
1956    {
1957#ifdef DEBUG
1958	fprintf(stderr,
1959		"fsQueryXBitmaps16: num_chars (%u) > bufleft (%ld) / %d\n",
1960		(unsigned) rep->num_chars, bufleft, SIZEOF (fsOffset32));
1961#endif
1962	err = AllocError;
1963	goto bail;
1964    }
1965    ppbits = (fsOffset32 *) buf;
1966    buf += SIZEOF (fsOffset32) * (rep->num_chars);
1967    bufleft -= SIZEOF (fsOffset32) * (rep->num_chars);
1968
1969    if (bufleft < rep->nbytes)
1970    {
1971#ifdef DEBUG
1972	fprintf(stderr,
1973		"fsQueryXBitmaps16: nbytes (%u) > bufleft (%ld)\n",
1974		(unsigned) rep->nbytes, bufleft);
1975#endif
1976	err = AllocError;
1977	goto bail;
1978    }
1979    pbitmaps = (pointer ) buf;
1980
1981    if (blockrec->type == FS_LOAD_GLYPHS)
1982    {
1983	nranges = bglyph->num_expected_ranges;
1984	nextrange = bglyph->expected_ranges;
1985    }
1986
1987    /* place the incoming glyphs */
1988    if (nranges)
1989    {
1990	/* We're operating under the assumption that the ranges
1991	   requested in the LoadGlyphs call were all legal for this
1992	   font, and that individual ranges do not cover multiple
1993	   rows...  fs_build_range() is designed to ensure this. */
1994	minchar = (nextrange->min_char_high - pfi->firstRow) *
1995		  (pfi->lastCol - pfi->firstCol + 1) +
1996		  nextrange->min_char_low - pfi->firstCol;
1997	maxchar = (nextrange->max_char_high - pfi->firstRow) *
1998		  (pfi->lastCol - pfi->firstCol + 1) +
1999		  nextrange->max_char_low - pfi->firstCol;
2000	nextrange++;
2001    }
2002    else
2003    {
2004	minchar = 0;
2005	maxchar = rep->num_chars;
2006    }
2007
2008    off_adr = (char *)ppbits;
2009
2010    allbits = fs_alloc_glyphs (pfont, rep->nbytes);
2011
2012    if (!allbits)
2013    {
2014	err = AllocError;
2015	goto bail;
2016    }
2017
2018#ifdef DEBUG
2019    origallbits = allbits;
2020    fprintf (stderr, "Reading %d glyphs in %d bytes for %s\n",
2021	     (int) rep->num_chars, (int) rep->nbytes, fsd->name);
2022#endif
2023
2024    for (i = 0; i < rep->num_chars; i++)
2025    {
2026	memcpy(&local_off, off_adr, SIZEOF(fsOffset32));	/* align it */
2027	if (blockrec->type == FS_OPEN_FONT ||
2028	    fsdata->encoding[minchar].bits == &_fs_glyph_requested)
2029	{
2030	    /*
2031	     * Broken X font server returns bits for missing characters
2032	     * when font is padded
2033	     */
2034	    if (NONZEROMETRICS(&fsdata->encoding[minchar].metrics))
2035	    {
2036		if (local_off.length &&
2037		    (local_off.position < rep->nbytes) &&
2038		    (local_off.length <= (rep->nbytes - local_off.position)))
2039		{
2040		    bits = allbits;
2041		    allbits += local_off.length;
2042		    memcpy(bits, (char *)pbitmaps + local_off.position,
2043			   local_off.length);
2044		}
2045		else
2046		    bits = &_fs_glyph_zero_length;
2047	    }
2048	    else
2049		bits = 0;
2050	    if (fsdata->encoding[minchar].bits == &_fs_glyph_requested)
2051		fsd->glyphs_to_get--;
2052	    fsdata->encoding[minchar].bits = bits;
2053	}
2054	if (minchar++ == maxchar)
2055	{
2056	    if (!--nranges) break;
2057	    minchar = (nextrange->min_char_high - pfi->firstRow) *
2058		      (pfi->lastCol - pfi->firstCol + 1) +
2059		      nextrange->min_char_low - pfi->firstCol;
2060	    maxchar = (nextrange->max_char_high - pfi->firstRow) *
2061		      (pfi->lastCol - pfi->firstCol + 1) +
2062		      nextrange->max_char_low - pfi->firstCol;
2063	    nextrange++;
2064	}
2065	off_adr += SIZEOF(fsOffset32);
2066    }
2067#ifdef DEBUG
2068    fprintf (stderr, "Used %d bytes instead of %d\n",
2069	     (int) (allbits - origallbits), (int) rep->nbytes);
2070#endif
2071
2072    if (blockrec->type == FS_OPEN_FONT)
2073    {
2074	fsd->glyphs_to_get = 0;
2075	bfont->state = FS_DONE_REPLY;
2076    }
2077    err = Successful;
2078
2079bail:
2080    if (rep)
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 (%u * %d) > bufleft (%ld)\n",
2577		(unsigned) 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 (%u) > bufleft (%ld)\n",
2589		(unsigned) 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	}
2860	crac.length = (sizeof (fsCreateACReq) + authlen) >> 2;
2861	crac.acid = cur->acid;
2862	_fs_add_req_log(conn, FS_CreateAC);
2863	_fs_write(conn, (char *) &crac, sizeof (fsCreateACReq));
2864	_fs_write_pad(conn, authorizations, authlen);
2865	/* ignore reply; we don't even care about it */
2866	conn->curacid = 0;
2867	cur->auth_generation = client_auth_generation(client);
2868    }
2869    if (conn->curacid != cur->acid)
2870    {
2871    	setac.reqType = FS_SetAuthorization;
2872	setac.pad = 0;
2873    	setac.length = sizeof (fsSetAuthorizationReq) >> 2;
2874    	setac.id = cur->acid;
2875    	_fs_add_req_log(conn, FS_SetAuthorization);
2876    	_fs_write(conn, (char *) &setac, sizeof (fsSetAuthorizationReq));
2877	conn->curacid = cur->acid;
2878    }
2879}
2880
2881/*
2882 * Poll a pending connect
2883 */
2884
2885static int
2886_fs_check_connect (FSFpePtr conn)
2887{
2888    int	    ret;
2889
2890    ret = _fs_poll_connect (conn->trans_conn, 0);
2891    switch (ret) {
2892    case FSIO_READY:
2893	conn->fs_fd = _FontTransGetConnectionNumber (conn->trans_conn);
2894	conn_start_listening(conn);
2895	break;
2896    case FSIO_BLOCK:
2897	break;
2898    }
2899    return ret;
2900}
2901
2902/*
2903 * Return an FSIO status while waiting for the completed connection
2904 * reply to arrive
2905 */
2906
2907static fsConnSetup *
2908_fs_get_conn_setup (FSFpePtr conn, int *error, int *setup_len)
2909{
2910    int			ret;
2911    char		*data;
2912    int			headlen;
2913    int			len;
2914    fsConnSetup		*setup;
2915    fsConnSetupAccept	*accept;
2916
2917    ret = _fs_start_read (conn, SIZEOF (fsConnSetup), &data);
2918    if (ret != FSIO_READY)
2919    {
2920	*error = ret;
2921	return 0;
2922    }
2923
2924    setup = (fsConnSetup *) data;
2925    if (setup->major_version > FS_PROTOCOL)
2926    {
2927	*error = FSIO_ERROR;
2928	return 0;
2929    }
2930
2931    headlen = (SIZEOF (fsConnSetup) +
2932	       (setup->alternate_len << 2) +
2933	       (setup->auth_len << 2));
2934    /* On anything but Success, no extra data is sent */
2935    if (setup->status != AuthSuccess)
2936    {
2937	len = headlen;
2938    }
2939    else
2940    {
2941	ret = _fs_start_read (conn, headlen + SIZEOF (fsConnSetupAccept), &data);
2942	if (ret != FSIO_READY)
2943	{
2944	    *error = ret;
2945	    return 0;
2946	}
2947	setup = (fsConnSetup *) data;
2948	accept = (fsConnSetupAccept *) (data + headlen);
2949	len = headlen + (accept->length << 2);
2950    }
2951    ret = _fs_start_read (conn, len, &data);
2952    if (ret != FSIO_READY)
2953    {
2954	*error = ret;
2955	return 0;
2956    }
2957    *setup_len = len;
2958    return (fsConnSetup *) data;
2959}
2960
2961static int
2962_fs_send_conn_client_prefix (FSFpePtr conn)
2963{
2964    fsConnClientPrefix	req;
2965    int			endian;
2966    int			ret;
2967
2968    /* send setup prefix */
2969    endian = 1;
2970    if (*(char *) &endian)
2971	req.byteOrder = 'l';
2972    else
2973	req.byteOrder = 'B';
2974
2975    req.major_version = FS_PROTOCOL;
2976    req.minor_version = FS_PROTOCOL_MINOR;
2977
2978/* XXX add some auth info here */
2979    req.num_auths = 0;
2980    req.auth_len = 0;
2981    ret = _fs_write (conn, (char *) &req, SIZEOF (fsConnClientPrefix));
2982    if (ret != FSIO_READY)
2983	return FSIO_ERROR;
2984    conn->blockedConnectTime = GetTimeInMillis () + FontServerRequestTimeout;
2985    return ret;
2986}
2987
2988static int
2989_fs_recv_conn_setup (FSFpePtr conn)
2990{
2991    int			ret = FSIO_ERROR;
2992    fsConnSetup		*setup;
2993    FSFpeAltPtr		alts;
2994    unsigned int	i, alt_len;
2995    int			setup_len;
2996    char		*alt_save, *alt_names;
2997
2998    setup = _fs_get_conn_setup (conn, &ret, &setup_len);
2999    if (!setup)
3000	return ret;
3001    conn->current_seq = 0;
3002    conn->fsMajorVersion = setup->major_version;
3003    /*
3004     * Create an alternate list from the initial server, but
3005     * don't chain looking for alternates.
3006     */
3007    if (conn->alternate == 0)
3008    {
3009	/*
3010	 * free any existing alternates list, allowing the list to
3011	 * be updated
3012	 */
3013	if (conn->alts)
3014	{
3015	    free (conn->alts);
3016	    conn->alts = 0;
3017	    conn->numAlts = 0;
3018	}
3019	if (setup->num_alternates)
3020	{
3021	    size_t alt_name_len = setup->alternate_len << 2;
3022	    alts = malloc (setup->num_alternates * sizeof (FSFpeAltRec) +
3023			   alt_name_len);
3024	    if (alts)
3025	    {
3026		alt_names = (char *) (setup + 1);
3027		alt_save = (char *) (alts + setup->num_alternates);
3028		for (i = 0; i < setup->num_alternates; i++)
3029		{
3030		    alts[i].subset = alt_names[0];
3031		    alt_len = alt_names[1];
3032		    if (alt_len >= alt_name_len) {
3033			/*
3034			 * Length is longer than setup->alternate_len
3035			 * told us to allocate room for, assume entire
3036			 * alternate list is corrupted.
3037			 */
3038#ifdef DEBUG
3039			fprintf (stderr,
3040				 "invalid alt list (length %lx >= %lx)\n",
3041				 (long) alt_len, (long) alt_name_len);
3042#endif
3043			free(alts);
3044			return FSIO_ERROR;
3045		    }
3046		    alts[i].name = alt_save;
3047		    memcpy (alt_save, alt_names + 2, alt_len);
3048		    alt_save[alt_len] = '\0';
3049		    alt_save += alt_len + 1;
3050		    alt_name_len -= alt_len + 1;
3051		    alt_names += _fs_pad_length (alt_len + 2);
3052		}
3053		conn->numAlts = setup->num_alternates;
3054		conn->alts = alts;
3055	    }
3056	}
3057    }
3058    _fs_done_read (conn, setup_len);
3059    if (setup->status != AuthSuccess)
3060	return FSIO_ERROR;
3061    return FSIO_READY;
3062}
3063
3064static int
3065_fs_open_server (FSFpePtr conn)
3066{
3067    int	    ret;
3068    char    *servername;
3069
3070    if (conn->alternate == 0)
3071	servername = conn->servername;
3072    else
3073	servername = conn->alts[conn->alternate-1].name;
3074    conn->trans_conn = _fs_connect (servername, &ret);
3075    conn->blockedConnectTime = GetTimeInMillis () + FS_RECONNECT_WAIT;
3076    return ret;
3077}
3078
3079static char *
3080_fs_catalog_name (char *servername)
3081{
3082    char    *sp;
3083
3084    sp = strchr (servername, '/');
3085    if (!sp)
3086	return 0;
3087    return strrchr (sp + 1, '/');
3088}
3089
3090static int
3091_fs_send_init_packets (FSFpePtr conn)
3092{
3093    fsSetResolutionReq	    srreq;
3094    fsSetCataloguesReq	    screq;
3095    int			    num_cats,
3096			    clen;
3097    char		    *catalogues;
3098    char		    *cat;
3099    char		    len;
3100    char		    *end;
3101    int			    num_res;
3102    FontResolutionPtr	    res;
3103
3104#define	CATALOGUE_SEP	'+'
3105
3106    res = GetClientResolutions(&num_res);
3107    if (num_res)
3108    {
3109	srreq.reqType = FS_SetResolution;
3110	srreq.num_resolutions = num_res;
3111	srreq.length = (SIZEOF(fsSetResolutionReq) +
3112			(num_res * SIZEOF(fsResolution)) + 3) >> 2;
3113
3114	_fs_add_req_log(conn, FS_SetResolution);
3115	if (_fs_write(conn, (char *) &srreq, SIZEOF(fsSetResolutionReq)) != FSIO_READY)
3116	    return FSIO_ERROR;
3117	if (_fs_write_pad(conn, (char *) res, (num_res * SIZEOF(fsResolution))) != FSIO_READY)
3118	    return FSIO_ERROR;
3119    }
3120
3121    catalogues = 0;
3122    if (conn->alternate != 0)
3123	catalogues = _fs_catalog_name (conn->alts[conn->alternate-1].name);
3124    if (!catalogues)
3125	catalogues = _fs_catalog_name (conn->servername);
3126
3127    if (!catalogues)
3128    {
3129	conn->has_catalogues = FALSE;
3130	return FSIO_READY;
3131    }
3132    conn->has_catalogues = TRUE;
3133
3134    /* turn cats into counted list */
3135    catalogues++;
3136
3137    cat = catalogues;
3138    num_cats = 0;
3139    clen = 0;
3140    while (*cat)
3141    {
3142	num_cats++;
3143	end = strchr(cat, CATALOGUE_SEP);
3144	if (!end)
3145	    end = cat + strlen (cat);
3146	clen += (end - cat) + 1;	/* length byte + string */
3147	cat = end;
3148    }
3149
3150    screq.reqType = FS_SetCatalogues;
3151    screq.num_catalogues = num_cats;
3152    screq.length = (SIZEOF(fsSetCataloguesReq) + clen + 3) >> 2;
3153
3154    _fs_add_req_log(conn, FS_SetCatalogues);
3155    if (_fs_write(conn, (char *) &screq, SIZEOF(fsSetCataloguesReq)) != FSIO_READY)
3156	return FSIO_ERROR;
3157
3158    while (*cat)
3159    {
3160	num_cats++;
3161	end = strchr(cat, CATALOGUE_SEP);
3162	if (!end)
3163	    end = cat + strlen (cat);
3164	len = end - cat;
3165	if (_fs_write (conn, &len, 1) != FSIO_READY)
3166	    return FSIO_ERROR;
3167	if (_fs_write (conn, cat, (int) len) != FSIO_READY)
3168	    return FSIO_ERROR;
3169	cat = end;
3170    }
3171
3172    if (_fs_write (conn, "....", _fs_pad_length (clen) - clen) != FSIO_READY)
3173	return FSIO_ERROR;
3174
3175    return FSIO_READY;
3176}
3177
3178static int
3179_fs_send_cat_sync (FSFpePtr conn)
3180{
3181    fsListCataloguesReq	    lcreq;
3182
3183    /*
3184     * now sync up with the font server, to see if an error was generated
3185     * by a bogus catalogue
3186     */
3187    lcreq.reqType = FS_ListCatalogues;
3188    lcreq.data = 0;
3189    lcreq.length = (SIZEOF(fsListCataloguesReq)) >> 2;
3190    lcreq.maxNames = 0;
3191    lcreq.nbytes = 0;
3192    lcreq.pad2 = 0;
3193    _fs_add_req_log(conn, FS_SetCatalogues);
3194    if (_fs_write(conn, (char *) &lcreq, SIZEOF(fsListCataloguesReq)) != FSIO_READY)
3195	return FSIO_ERROR;
3196    conn->blockedConnectTime = GetTimeInMillis () + FontServerRequestTimeout;
3197    return FSIO_READY;
3198}
3199
3200static int
3201_fs_recv_cat_sync (FSFpePtr conn)
3202{
3203    fsGenericReply  *reply;
3204    fsError	    *error;
3205    int		    err;
3206    int		    ret;
3207
3208    reply = fs_get_reply (conn, &err);
3209    if (!reply)
3210	return err;
3211
3212    ret = FSIO_READY;
3213    if (reply->type == FS_Error)
3214    {
3215	error = (fsError *) reply;
3216	if (error->major_opcode == FS_SetCatalogues)
3217	    ret = FSIO_ERROR;
3218    }
3219    _fs_done_read (conn, reply->length << 2);
3220    return ret;
3221}
3222
3223static void
3224_fs_close_server (FSFpePtr conn)
3225{
3226    _fs_unmark_block (conn, FS_PENDING_WRITE|FS_BROKEN_WRITE|FS_COMPLETE_REPLY|FS_BROKEN_CONNECTION);
3227    if (conn->trans_conn)
3228    {
3229	_FontTransClose (conn->trans_conn);
3230	conn->trans_conn = 0;
3231	_fs_io_reinit (conn);
3232    }
3233    if (conn->fs_fd >= 0)
3234    {
3235	conn_stop_listening(conn);
3236	conn->fs_fd = -1;
3237    }
3238    conn->fs_conn_state = FS_CONN_UNCONNECTED;
3239}
3240
3241static int
3242_fs_do_setup_connection (FSFpePtr conn)
3243{
3244    int	    ret;
3245
3246    do
3247    {
3248#ifdef DEBUG
3249	fprintf (stderr, "fs_do_setup_connection state %d\n", conn->fs_conn_state);
3250#endif
3251	switch (conn->fs_conn_state) {
3252	case FS_CONN_UNCONNECTED:
3253	    ret = _fs_open_server (conn);
3254	    if (ret == FSIO_BLOCK)
3255		conn->fs_conn_state = FS_CONN_CONNECTING;
3256	    break;
3257	case FS_CONN_CONNECTING:
3258	    ret = _fs_check_connect (conn);
3259	    break;
3260	case FS_CONN_CONNECTED:
3261	    ret = _fs_send_conn_client_prefix (conn);
3262	    break;
3263	case FS_CONN_SENT_PREFIX:
3264	    ret = _fs_recv_conn_setup (conn);
3265	    break;
3266	case FS_CONN_RECV_INIT:
3267	    ret = _fs_send_init_packets (conn);
3268	    if (conn->has_catalogues)
3269		ret = _fs_send_cat_sync (conn);
3270	    break;
3271	case FS_CONN_SENT_CAT:
3272	    if (conn->has_catalogues)
3273		ret = _fs_recv_cat_sync (conn);
3274	    else
3275		ret = FSIO_READY;
3276	    break;
3277	default:
3278	    ret = FSIO_READY;
3279	    break;
3280	}
3281	switch (ret) {
3282	case FSIO_READY:
3283	    if (conn->fs_conn_state < FS_CONN_RUNNING)
3284		conn->fs_conn_state++;
3285	    break;
3286	case FSIO_BLOCK:
3287	    if (TimeCmp (GetTimeInMillis (), <, conn->blockedConnectTime))
3288		break;
3289	    ret = FSIO_ERROR;
3290	    /* fall through... */
3291	case FSIO_ERROR:
3292	    _fs_close_server (conn);
3293	    /*
3294	     * Try the next alternate
3295	     */
3296	    if (conn->alternate < conn->numAlts)
3297	    {
3298		conn->alternate++;
3299		ret = FSIO_READY;
3300	    }
3301	    else
3302		conn->alternate = 0;
3303	    break;
3304	}
3305    } while (conn->fs_conn_state != FS_CONN_RUNNING && ret == FSIO_READY);
3306    if (ret == FSIO_READY)
3307	conn->generation = ++generationCount;
3308    return ret;
3309}
3310
3311static int
3312_fs_wait_connect (FSFpePtr conn)
3313{
3314    int	    ret;
3315
3316    for (;;)
3317    {
3318	ret = _fs_do_setup_connection (conn);
3319	if (ret != FSIO_BLOCK)
3320	    break;
3321	if (conn->fs_conn_state <= FS_CONN_CONNECTING)
3322	    ret = _fs_poll_connect (conn->trans_conn, 1000);
3323	else
3324	    ret = _fs_wait_for_readable (conn, 1000);
3325	if (ret == FSIO_ERROR)
3326	    break;
3327    }
3328    return ret;
3329}
3330
3331/*
3332 * Poll a connection in the process of reconnecting
3333 */
3334static void
3335_fs_check_reconnect (FSFpePtr conn)
3336{
3337    int	    ret;
3338
3339    ret = _fs_do_setup_connection (conn);
3340    switch (ret) {
3341    case FSIO_READY:
3342	_fs_unmark_block (conn, FS_RECONNECTING|FS_GIVE_UP);
3343	_fs_restart_connection (conn);
3344	break;
3345    case FSIO_BLOCK:
3346	break;
3347    case FSIO_ERROR:
3348	conn->brokenConnectionTime = GetTimeInMillis () + FS_RECONNECT_POLL;
3349	break;
3350    }
3351}
3352
3353/*
3354 * Start the reconnection process
3355 */
3356static void
3357_fs_start_reconnect (FSFpePtr conn)
3358{
3359    if (conn->blockState & FS_RECONNECTING)
3360	return;
3361    conn->alternate = 0;
3362    _fs_mark_block (conn, FS_RECONNECTING);
3363    _fs_unmark_block (conn, FS_BROKEN_CONNECTION);
3364    _fs_check_reconnect (conn);
3365}
3366
3367
3368static FSFpePtr
3369_fs_init_conn (const char *servername, FontPathElementPtr fpe)
3370{
3371    FSFpePtr	conn;
3372    size_t	snlen = strlen (servername) + 1;
3373
3374    conn = calloc (1, sizeof (FSFpeRec) + snlen);
3375    if (!conn)
3376	return 0;
3377    if (!_fs_io_init (conn))
3378    {
3379	free (conn);
3380	return 0;
3381    }
3382    conn->servername = (char *) (conn + 1);
3383    conn->fs_conn_state = FS_CONN_UNCONNECTED;
3384    conn->fs_fd = -1;
3385    conn->fpe = fpe;
3386    strlcpy (conn->servername, servername, snlen);
3387    return conn;
3388}
3389
3390static void
3391_fs_free_conn (FSFpePtr conn)
3392{
3393    _fs_close_server (conn);
3394    _fs_io_fini (conn);
3395    if (conn->alts)
3396	free (conn->alts);
3397    free (conn);
3398}
3399
3400/*
3401 * called at server init time
3402 */
3403
3404static const xfont2_fpe_funcs_rec fs_fpe_funcs = {
3405	.version = XFONT2_FPE_FUNCS_VERSION,
3406	.name_check = fs_name_check,
3407	.init_fpe = fs_init_fpe,
3408	.free_fpe = fs_free_fpe,
3409	.reset_fpe = fs_reset_fpe,
3410	.open_font = fs_open_font,
3411	.close_font = fs_close_font,
3412	.list_fonts = fs_list_fonts,
3413	.start_list_fonts_with_info = fs_start_list_with_info,
3414	.list_next_font_with_info = fs_next_list_with_info,
3415	.wakeup_fpe = fs_wakeup,
3416	.client_died = fs_client_died,
3417	.load_glyphs = _fs_load_glyphs,
3418	.start_list_fonts_and_aliases = (StartLaFunc) 0,
3419	.list_next_font_or_alias = (NextLaFunc) 0,
3420	.set_path_hook = (SetPathFunc) 0
3421};
3422
3423void
3424fs_register_fpe_functions(void)
3425{
3426    register_fpe_funcs(&fs_fpe_funcs);
3427}
3428