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