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