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