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