1 /*
2 XRecord.c - client-side library for RECORD extension
3 
4 Copyright 1995, 1998  The Open Group
5 
6 Permission to use, copy, modify, distribute, and sell this software and its
7 documentation for any purpose is hereby granted without fee, provided that
8 the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
10 documentation.
11 
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 OTHER DEALINGS IN THE SOFTWARE.
22 
23 Except as contained in this notice, the name of The Open Group shall
24 not be used in advertising or otherwise to promote the sale, use or
25 other dealings in this Software without prior written authorization
26 from The Open Group.
27 
28 */
29 /***************************************************************************
30  * Copyright 1995 Network Computing Devices
31  *
32  * Permission to use, copy, modify, distribute, and sell this software and
33  * its documentation for any purpose is hereby granted without fee, provided
34  * that the above copyright notice appear in all copies and that both that
35  * copyright notice and this permission notice appear in supporting
36  * documentation, and that the name of Network Computing Devices
37  * not be used in advertising or publicity pertaining to distribution
38  * of the software without specific, written prior permission.
39  *
40  * NETWORK COMPUTING DEVICES DISCLAIMs ALL WARRANTIES WITH REGARD TO
41  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
42  * AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE
43  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
44  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
45  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
46  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
47  **************************************************************************/
48 /*
49  * By Stephen Gildea, X Consortium, and Martha Zimet, NCD.
50  */
51 
52 #ifdef HAVE_CONFIG_H
53 #include <config.h>
54 #endif
55 #include <stdio.h>
56 #include <assert.h>
57 #include <X11/Xlibint.h>
58 #include <X11/extensions/Xext.h>
59 #include <X11/extensions/extutil.h>
60 #include <X11/extensions/recordproto.h>
61 #include <X11/extensions/record.h>
62 #include <limits.h>
63 
64 static XExtensionInfo _xrecord_info_data;
65 static XExtensionInfo *xrecord_info = &_xrecord_info_data;
66 static const char *xrecord_extension_name = RECORD_NAME;
67 
68 #define XRecordCheckExtension(dpy,i,val) \
69     XextCheckExtension(dpy, i, xrecord_extension_name, val)
70 
71 /**************************************************************************
72  *                                                                        *
73  *			   private utility routines                       *
74  *                                                                        *
75  **************************************************************************/
76 
77 static XExtDisplayInfo *find_display(Display *dpy);
78 
79 /*
80  * A reply buffer holds a reply from RecordEnableContext.
81  * Pieces of that buffer are passed to the XRecordEnableContext callback.
82  * ref_count is incremented each time we do that.
83  * ref_count is decremented each time XRecordFreeData is called on
84  * the buffer.  When ref_count is 0, we can free or reuse the buffer.
85  */
86 struct reply_buffer
87 {
88     struct reply_buffer *next;	/* next in list or NULL */
89     unsigned char *buf;		/* pointer to malloc'd buffer */
90     int nbytes;			/* size of buf */
91     int ref_count;		/* callback uses pending */
92 };
93 
94 
95 /*
96  * There's some extra information the implementation finds useful
97  * to attach to an XRecordInterceptData packet to handle memory
98  * management.  So we really allocate one of these.
99  */
100 struct intercept_queue
101 {
102     /* this struct gets passed to the user as an XRecordInterceptData,
103        so the data field must come first so we can cast the address
104        back and forth */
105     XRecordInterceptData data;
106     struct intercept_queue *next; /* next in free list or NULL */
107     struct mem_cache_str *cache; /* contains head of free list */
108 };
109 
110 /*
111  * per-display pointers to cache of malloc'd but unused memory
112  */
113 struct mem_cache_str
114 {
115     struct intercept_queue *inter_data;	/* free structs only */
116     struct reply_buffer *reply_buffers;	/* all reply buffers */
117     int inter_data_count;	/* total allocated, free and in use */
118     Bool display_closed;	/* so we know when to free ourself */
119 };
120 
close_display(Display * dpy,XExtCodes * codes)121 static int close_display(
122     Display *dpy,
123     XExtCodes *codes)		/* not used */
124 {
125     XExtDisplayInfo *info = find_display (dpy);
126 
127     LockDisplay(dpy);
128     if (info && info->data) {
129 	struct mem_cache_str *cache = (struct mem_cache_str *)info->data;
130 	struct intercept_queue *iq, *iq_next;
131 	struct reply_buffer *rbp, **rbp_next_p;
132 
133 	for (iq=cache->inter_data; iq; iq=iq_next) {
134 	    iq_next = iq->next;
135 	    XFree(iq);
136 	    cache->inter_data_count--;
137 	}
138 
139 	/* this is a little trickier, because some of these
140 	   might still be in use */
141 	for (rbp_next_p = &cache->reply_buffers; *rbp_next_p; ) {
142 	    rbp = *rbp_next_p;
143 	    if (rbp->ref_count == 0) {
144 		*rbp_next_p = rbp->next;
145 		XFree(rbp->buf);
146 		XFree(rbp);
147 	    } else {
148 		rbp_next_p = &rbp->next;
149 	    }
150 	}
151 
152 	if (cache->reply_buffers == NULL  &&  cache->inter_data_count == 0) {
153 	    /* every thing has been freed, can free ourselves, too */
154 	    XFree(cache);
155 	} else {
156 	    cache->display_closed = True;
157 	    cache->inter_data = NULL; /* neatness only; won't be used */
158 	}
159     }
160     UnlockDisplay(dpy);
161     return XextRemoveDisplay(xrecord_info, dpy);
162 }
163 
alloc_mem_cache(void)164 static XPointer alloc_mem_cache(void)
165 {
166     struct mem_cache_str *cache;
167 
168     /* note that an error will go unnoticed */
169     cache = (struct mem_cache_str *) Xmalloc(sizeof(struct mem_cache_str));
170     if (cache) {
171 	cache->display_closed = False;
172 	cache->inter_data = NULL;
173 	cache->inter_data_count = 0;
174 	cache->reply_buffers = NULL;
175     }
176     return (XPointer) cache;
177 }
178 
179 static const char *xrecord_error_list[] = {
180     "XRecordBadContext (Not a defined RECORD context)",
181 };
182 
183 static XEXT_GENERATE_ERROR_STRING (error_string, xrecord_extension_name,
184                                    RecordNumErrors, xrecord_error_list)
185 
186 static XExtensionHooks xrecord_extension_hooks = {
187     NULL,                               /* create_gc */
188     NULL,                               /* copy_gc */
189     NULL,                               /* flush_gc */
190     NULL,                               /* free_gc */
191     NULL,                               /* create_font */
192     NULL,                               /* free_font */
193     close_display,                      /* close_display */
194     NULL,                      		/* wire_to_event */
195     NULL,                      		/* event_to_wire */
196     NULL,                               /* error */
197     error_string                        /* error_string */
198 };
199 
200 static XEXT_GENERATE_FIND_DISPLAY (find_display, xrecord_info,
201 	xrecord_extension_name, &xrecord_extension_hooks, RecordNumEvents,
202 	alloc_mem_cache())
203 
204 /**************************************************************************
205  *                                                                        *
206  *			   private library routines                       *
207  *                                                                        *
208  **************************************************************************/
209 
210 static void
SendRange(Display * dpy,XRecordRange ** range_item,int nranges)211 SendRange(
212     Display 	*dpy,
213     XRecordRange **range_item,
214     int   	nranges)
215 {
216     int 		rlen = SIZEOF(xRecordRange);
217     while(nranges--)
218     {
219        xRecordRange xrange;
220 
221        xrange.coreRequestsFirst = (*range_item)->core_requests.first;
222        xrange.coreRequestsLast = (*range_item)->core_requests.last;
223        xrange.coreRepliesFirst = (*range_item)->core_replies.first;
224        xrange.coreRepliesLast = (*range_item)->core_replies.last;
225        xrange.extRequestsMajorFirst = (*range_item)->ext_requests.ext_major.first;
226        xrange.extRequestsMajorLast = (*range_item)->ext_requests.ext_major.last;
227        xrange.extRequestsMinorFirst = (*range_item)->ext_requests.ext_minor.first;
228        xrange.extRequestsMinorLast = (*range_item)->ext_requests.ext_minor.last;
229        xrange.extRepliesMajorFirst = (*range_item)->ext_replies.ext_major.first;
230        xrange.extRepliesMajorLast = (*range_item)->ext_replies.ext_major.last;
231        xrange.extRepliesMinorFirst = (*range_item)->ext_replies.ext_minor.first;
232        xrange.extRepliesMinorLast = (*range_item)->ext_replies.ext_minor.last;
233        xrange.deliveredEventsFirst = (*range_item)->delivered_events.first;
234        xrange.deliveredEventsLast = (*range_item)->delivered_events.last;
235        xrange.deviceEventsFirst = (*range_item)->device_events.first;
236        xrange.deviceEventsLast = (*range_item)->device_events.last;
237        xrange.errorsFirst = (*range_item)->errors.first;
238        xrange.errorsLast = (*range_item)->errors.last;
239        xrange.clientStarted = (*range_item)->client_started;
240        xrange.clientDied = (*range_item)->client_died;
241 
242        Data(dpy, (char *)&xrange, rlen);
243        range_item++;
244     }
245 }
246 
247 /**************************************************************************
248  *                                                                        *
249  *		    public routines               			  *
250  *                                                                        *
251  **************************************************************************/
252 
253 XID
XRecordIdBaseMask(Display * dpy)254 XRecordIdBaseMask(Display *dpy)
255 {
256     return 0x1fffffff & ~dpy->resource_mask;
257 }
258 
259 Status
XRecordQueryVersion(Display * dpy,int * cmajor_return,int * cminor_return)260 XRecordQueryVersion(Display *dpy, int *cmajor_return, int *cminor_return)
261 {
262     XExtDisplayInfo *info = find_display (dpy);
263     register xRecordQueryVersionReq   	*req;
264     xRecordQueryVersionReply 		rep;
265 
266     XRecordCheckExtension (dpy, info, False);
267 
268     LockDisplay(dpy);
269     GetReq(RecordQueryVersion, req);
270     req->reqType = info->codes->major_opcode;
271     req->recordReqType = X_RecordQueryVersion;
272     req->majorVersion = RECORD_MAJOR_VERSION;
273     req->minorVersion = RECORD_MINOR_VERSION;
274     if (!_XReply(dpy,(xReply *)&rep, 0, True)) {
275 	UnlockDisplay(dpy);
276 	SyncHandle();
277 	return False;
278     }
279     UnlockDisplay(dpy);
280     SyncHandle();
281     *cmajor_return = rep.majorVersion;
282     *cminor_return = rep.minorVersion;
283     return ((rep.majorVersion == RECORD_MAJOR_VERSION) &&
284 	    (rep.minorVersion >= RECORD_LOWEST_MINOR_VERSION));
285 }
286 
287 XRecordContext
XRecordCreateContext(Display * dpy,int datum_flags,XRecordClientSpec * clients,int nclients,XRecordRange ** ranges,int nranges)288 XRecordCreateContext(Display *dpy, int datum_flags,
289 		     XRecordClientSpec *clients, int nclients,
290 		     XRecordRange **ranges, int nranges)
291 {
292     XExtDisplayInfo 	*info = find_display (dpy);
293     register xRecordCreateContextReq 	*req;
294     int			clen = 4 * nclients;
295 
296     XRecordCheckExtension (dpy, info, 0);
297     LockDisplay(dpy);
298     GetReq(RecordCreateContext, req);
299 
300     req->reqType = info->codes->major_opcode;
301     req->recordReqType = X_RecordCreateContext;
302     req->context = XAllocID(dpy);
303     req->length += (nclients * 4 +
304 		    nranges * SIZEOF(xRecordRange)) >> 2;
305     req->elementHeader = datum_flags;
306     req->nClients = nclients;
307     req->nRanges = nranges;
308 
309     Data32(dpy, (long *)clients, clen);
310     SendRange(dpy, ranges, nranges);
311 
312     UnlockDisplay(dpy);
313     SyncHandle();
314     return req->context;
315 }
316 
317 XRecordRange *
XRecordAllocRange(void)318 XRecordAllocRange(void)
319 {
320     return (XRecordRange*)Xcalloc(1, sizeof(XRecordRange));
321 }
322 
323 Status
XRecordRegisterClients(Display * dpy,XRecordContext context,int datum_flags,XRecordClientSpec * clients,int nclients,XRecordRange ** ranges,int nranges)324 XRecordRegisterClients(Display *dpy, XRecordContext context, int datum_flags,
325 		       XRecordClientSpec *clients, int nclients,
326 		       XRecordRange **ranges, int nranges)
327 {
328     XExtDisplayInfo *info = find_display (dpy);
329     register xRecordRegisterClientsReq 	*req;
330     int			clen = 4 * nclients;
331 
332     XRecordCheckExtension (dpy, info, 0);
333     LockDisplay(dpy);
334     GetReq(RecordRegisterClients, req);
335 
336     req->reqType = info->codes->major_opcode;
337     req->recordReqType = X_RecordRegisterClients;
338     req->context =  context;
339     req->length += (nclients * 4 +
340 		    nranges * SIZEOF(xRecordRange)) >> 2;
341     req->elementHeader = datum_flags;
342     req->nClients = nclients;
343     req->nRanges = nranges;
344 
345     Data32(dpy, (long *)clients, clen);
346     SendRange(dpy, ranges, nranges);
347 
348     UnlockDisplay(dpy);
349     SyncHandle();
350     return 1;
351 }
352 
353 Status
XRecordUnregisterClients(Display * dpy,XRecordContext context,XRecordClientSpec * clients,int nclients)354 XRecordUnregisterClients(Display *dpy, XRecordContext context,
355 			 XRecordClientSpec *clients, int nclients)
356 {
357     XExtDisplayInfo *info = find_display (dpy);
358     register xRecordUnregisterClientsReq 	*req;
359     int			clen = 4 * nclients;
360 
361     XRecordCheckExtension (dpy, info, 0);
362     LockDisplay(dpy);
363     GetReq(RecordUnregisterClients, req);
364 
365     req->reqType = info->codes->major_opcode;
366     req->recordReqType = X_RecordUnregisterClients;
367     req->context = context;
368     req->length += nclients;
369     req->nClients = nclients;
370 
371     Data32(dpy, (long *)clients, clen);
372 
373     UnlockDisplay(dpy);
374     SyncHandle();
375     return 1;
376 }
377 
378 static void
WireToLibRange(xRecordRange * wire_range,XRecordRange * lib_range)379 WireToLibRange(
380     xRecordRange *wire_range,
381     XRecordRange *lib_range)
382 {
383     lib_range->core_requests.first = wire_range->coreRequestsFirst;
384     lib_range->core_requests.last = wire_range->coreRequestsLast;
385     lib_range->core_replies.first = wire_range->coreRepliesFirst;
386     lib_range->core_replies.last = wire_range->coreRepliesLast;
387     lib_range->ext_requests.ext_major.first = wire_range->extRequestsMajorFirst;
388     lib_range->ext_requests.ext_major.last = wire_range->extRequestsMajorLast;
389     lib_range->ext_requests.ext_minor.first = wire_range->extRequestsMinorFirst;
390     lib_range->ext_requests.ext_minor.last = wire_range->extRequestsMinorLast;
391     lib_range->ext_replies.ext_major.first = wire_range->extRepliesMajorFirst;
392     lib_range->ext_replies.ext_major.last = wire_range->extRepliesMajorLast;
393     lib_range->ext_replies.ext_minor.first = wire_range->extRepliesMinorFirst;
394     lib_range->ext_replies.ext_minor.last = wire_range->extRepliesMinorLast;
395     lib_range->delivered_events.first = wire_range->deliveredEventsFirst;
396     lib_range->delivered_events.last = wire_range->deliveredEventsLast;
397     lib_range->device_events.first = wire_range->deviceEventsFirst;
398     lib_range->device_events.last = wire_range->deviceEventsLast;
399     lib_range->errors.first = wire_range->errorsFirst;
400     lib_range->errors.last = wire_range->errorsLast;
401     lib_range->client_started = wire_range->clientStarted;
402     lib_range->client_died = wire_range->clientDied;
403 }
404 
405 Status
XRecordGetContext(Display * dpy,XRecordContext context,XRecordState ** state_return)406 XRecordGetContext(Display *dpy, XRecordContext context,
407 		  XRecordState **state_return)
408 {
409     XExtDisplayInfo 	*info = find_display (dpy);
410     register 		xRecordGetContextReq   	*req;
411     xRecordGetContextReply 	rep;
412     unsigned int	count, i, rn;
413     xRecordRange   	xrange;
414     xRecordClientInfo   xclient_inf;
415     XRecordState	*ret;
416 
417     XRecordCheckExtension (dpy, info, 0);
418     LockDisplay(dpy);
419     GetReq(RecordGetContext, req);
420     req->reqType = info->codes->major_opcode;
421     req->recordReqType = X_RecordGetContext;
422     req->context = context;
423     if (!_XReply(dpy,(xReply *)&rep, 0, False)) {
424 	UnlockDisplay(dpy);
425 	SyncHandle();
426 	return 0;
427     }
428     count = rep.nClients;
429 
430     ret = (XRecordState*)Xmalloc(sizeof(XRecordState));
431     if (!ret) {
432 	_XEatDataWords (dpy, rep.length);
433 	UnlockDisplay(dpy);
434 	SyncHandle();
435 	return 0;
436     }
437 
438     ret->enabled = rep.enabled;
439     ret->datum_flags = rep.elementHeader;
440     ret->nclients = count;
441 
442     if (count)
443     {
444 	XRecordClientInfo	**client_inf = NULL;
445 	XRecordClientInfo	*client_inf_str = NULL;
446 
447 	if (count < (INT_MAX / sizeof(XRecordClientInfo))) {
448 	    client_inf = Xcalloc(count, sizeof(XRecordClientInfo *));
449 	    if (client_inf != NULL)
450 		client_inf_str = Xmalloc(count * sizeof(XRecordClientInfo));
451 	}
452 	ret->client_info = client_inf;
453         if (!client_inf || !client_inf_str)
454         {
455 	   free(client_inf);
456 	   _XEatDataWords (dpy, rep.length);
457 	   UnlockDisplay(dpy);
458 	   XRecordFreeState(ret);
459 	   SyncHandle();
460 	   return 0;
461         }
462         for(i = 0; i < count; i++)
463         {
464 	    client_inf[i] = &(client_inf_str[i]);
465             _XRead(dpy, (char *)&xclient_inf, (long)sizeof(xRecordClientInfo));
466             client_inf_str[i].client = xclient_inf.clientResource;
467             client_inf_str[i].nranges = xclient_inf.nRanges;
468 
469 	    if (xclient_inf.nRanges)
470 	    {
471 		XRecordRange	*ranges = NULL;
472 
473 		if (xclient_inf.nRanges < (INT_MAX / sizeof(XRecordRange))) {
474 		    client_inf_str[i].ranges =
475 			Xcalloc(xclient_inf.nRanges, sizeof(XRecordRange *));
476 		    if (client_inf_str[i].ranges != NULL)
477 			ranges =
478 			    Xmalloc(xclient_inf.nRanges * sizeof(XRecordRange));
479 		}
480 		else
481 		    client_inf_str[i].ranges = NULL;
482 
483 		if (!client_inf_str[i].ranges || !ranges) {
484 		    /* XXX eat data */
485 		    UnlockDisplay(dpy);
486 		    XRecordFreeState(ret);
487 		    SyncHandle();
488 		    return 0;
489 		}
490 		for (rn=0; rn<xclient_inf.nRanges; rn++) {
491 		    client_inf_str[i].ranges[rn] = &(ranges[rn]);
492 		    _XRead(dpy, (char *)&xrange, (long)sizeof(xRecordRange));
493 		    WireToLibRange(&xrange, &(ranges[rn]));
494 		}
495 	    } else {
496 		client_inf_str[i].ranges = NULL;
497 	    }
498         }
499     } else {
500 	ret->client_info = NULL;
501     }
502 
503     *state_return = ret;
504 
505     UnlockDisplay(dpy);
506     SyncHandle();
507     return 1;
508 }
509 
510 void
XRecordFreeState(XRecordState * state)511 XRecordFreeState(XRecordState *state)
512 {
513     int i;
514 
515     for(i=0; i<state->nclients; i++) {
516 	if (state->client_info[i]->ranges) {
517 	    if (state->client_info[i]->ranges[0])
518 		Xfree(state->client_info[i]->ranges[0]);
519 	    Xfree(state->client_info[i]->ranges);
520 	}
521     }
522     if (state->client_info) {
523 	if (state->client_info[0])
524 	    Xfree(state->client_info[0]);
525 	Xfree(state->client_info);
526     }
527     Xfree(state);
528 }
529 
alloc_reply_buffer(XExtDisplayInfo * info,int nbytes)530 static struct reply_buffer *alloc_reply_buffer(
531     XExtDisplayInfo *info,
532     int nbytes)
533 {
534     struct mem_cache_str *cache = (struct mem_cache_str *)info->data;
535     struct reply_buffer *rbp;
536     struct reply_buffer *saved_rb = NULL;
537     /*
538      * First look for an allocated buffer that is not in use.
539      * If we have a big enough buffer, use that, otherwise
540      * realloc an existing one.
541      */
542     for (rbp = cache->reply_buffers; rbp; rbp = rbp->next) {
543 	if (rbp->ref_count == 0) {
544 	    if (rbp->nbytes >= nbytes)
545 		return rbp;
546 	    else
547 		saved_rb = rbp;
548 	}
549     }
550     if (saved_rb) {
551 	saved_rb->buf = (unsigned char *)Xrealloc(saved_rb->buf, nbytes);
552 	if (!saved_rb->buf) {
553 	    saved_rb->nbytes = 0;
554 	    return NULL;
555 	}
556 	saved_rb->nbytes = nbytes;
557 	return saved_rb;
558     }
559 
560     /*
561      * nothing available; malloc a new struct
562      */
563     rbp = (struct reply_buffer *)Xmalloc(sizeof(struct reply_buffer));
564     if (!rbp)
565 	return NULL;
566     rbp->buf = (unsigned char *)Xmalloc(nbytes);
567     if (!rbp->buf) {
568 	Xfree(rbp);
569 	return NULL;
570     }
571     rbp->nbytes = nbytes;
572     rbp->ref_count = 0;
573     rbp->next = cache->reply_buffers;
574     cache->reply_buffers = rbp;
575     return rbp;
576 }
577 
alloc_inter_data(XExtDisplayInfo * info)578 static XRecordInterceptData *alloc_inter_data(XExtDisplayInfo *info)
579 {
580     struct mem_cache_str *cache = (struct mem_cache_str *)info->data;
581     struct intercept_queue *iq;
582 
583     /* if there is one on the free list, pop it */
584     if (cache->inter_data) {
585 	iq = cache->inter_data;
586 	cache->inter_data = iq->next;
587 	return &iq->data;
588     }
589     /* allocate a new one */
590     iq = (struct intercept_queue *)Xmalloc(sizeof(struct intercept_queue));
591     if (!iq)
592 	return NULL;
593     iq->cache = cache;
594     cache->inter_data_count++;
595     return &iq->data;
596 }
597 
598 void
XRecordFreeData(XRecordInterceptData * data)599 XRecordFreeData(XRecordInterceptData *data)
600 {
601     /* we can do this cast because that is what we really allocated */
602     struct intercept_queue *iq = (struct intercept_queue *)data;
603     struct reply_buffer *rbp = NULL;
604     struct mem_cache_str *cache = iq->cache;
605 
606     /*
607      * figure out what reply_buffer this points at
608      * and decrement its ref_count.
609      */
610     if (data->data) {
611 
612 	for (rbp = cache->reply_buffers; rbp; rbp = rbp->next) {
613 	    if (data->data >= rbp->buf
614 		&& data->data < rbp->buf + rbp->nbytes)
615 	    {
616 		assert(rbp->ref_count > 0);
617 		rbp->ref_count--;
618 		break;
619 	    }
620 	}
621 	/* it's an error if we didn't find something to free */
622 	assert(rbp);
623     }
624     /*
625      * If the display is still open, put this back on the free queue.
626      *
627      * Otherwise the display is closed and we won't reuse this, so free it.
628      * See if we can free the reply buffer, too.
629      * If we can, see if this is the last reply buffer and if so
630      * free the list of reply buffers.
631      */
632     if (cache->display_closed == False) {
633 	iq->next = cache->inter_data;
634 	cache->inter_data = iq;
635     } else {
636 	if (rbp && rbp->ref_count == 0) {
637 	    struct reply_buffer *rbp2, **rbp_next_p;
638 
639 	    /* Have to search the list again to find the prev element.
640 	       This is not the common case, so don't slow down the code
641 	       above by doing it then. */
642 	    for (rbp_next_p = &cache->reply_buffers; *rbp_next_p; ) {
643 		rbp2 = *rbp_next_p;
644 		if (rbp == rbp2) {
645 		    *rbp_next_p = rbp2->next;
646 		    break;
647 		} else {
648 		    rbp_next_p = &rbp2->next;
649 		}
650 	    }
651 	    XFree(rbp->buf);
652 	    XFree(rbp);
653 	}
654 
655 	XFree(iq);
656 	cache->inter_data_count--;
657 
658 	if (cache->reply_buffers == NULL  &&  cache->inter_data_count == 0) {
659 	    XFree(cache); /* all finished */
660 	}
661     }
662 }
663 
664 /* the EXTRACT macros are adapted from ICElibint.h */
665 
666 #ifndef WORD64
667 
668 #define EXTRACT_CARD16(swap, src, dst) \
669 { \
670     (dst) = *((CARD16 *) (src)); \
671     if (swap) \
672         (dst) = lswaps (dst); \
673 }
674 
675 #define EXTRACT_CARD32(swap, src, dst) \
676 { \
677     (dst) = *((CARD32 *) (src)); \
678     if (swap) \
679         (dst) = lswapl (dst); \
680 }
681 
682 #else /* WORD64 */
683 
684 #define EXTRACT_CARD16(swap, src, dst) \
685 { \
686     (dst) = *((src) + 0); \
687     (dst) <<= 8; \
688     (dst) |= *((src) + 1); \
689     if (swap) \
690         (dst) = lswaps (dst); \
691 }
692 
693 #define EXTRACT_CARD32(swap, src, dst) \
694 { \
695     (dst) = *((src) + 0); \
696     (dst) <<= 8; \
697     (dst) |= *((src) + 1); \
698     (dst) <<= 8; \
699     (dst) |= *((src) + 2); \
700     (dst) <<= 8; \
701     (dst) |= *((src) + 3); \
702     if (swap) \
703         (dst) = lswapl (dst); \
704 }
705 
706 #endif /* WORD64 */
707 
708 /* byte swapping macros from xfs/include/misc.h */
709 
710 /* byte swap a long literal */
711 #define lswapl(x) ((((x) & 0xff) << 24) |\
712 		   (((x) & 0xff00) << 8) |\
713 		   (((x) & 0xff0000) >> 8) |\
714 		   (((x) >> 24) & 0xff))
715 
716 /* byte swap a short literal */
717 #define lswaps(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))
718 
719 enum parser_return { Continue, End, Error };
720 
721 static enum parser_return
parse_reply_call_callback(Display * dpy,XExtDisplayInfo * info,xRecordEnableContextReply * rep,struct reply_buffer * reply,XRecordInterceptProc callback,XPointer closure)722 parse_reply_call_callback(
723     Display *dpy,
724     XExtDisplayInfo *info,
725     xRecordEnableContextReply *rep,
726     struct reply_buffer *reply,
727     XRecordInterceptProc callback,
728     XPointer		 closure)
729 {
730     int current_index;
731     int datum_bytes = 0;
732     XRecordInterceptData *data;
733 
734     /* call the callback for each protocol element in the reply */
735     current_index = 0;
736     do {
737 	data = alloc_inter_data(info);
738 	if (!data)
739 	    return Error;
740 
741 	data->id_base = rep->idBase;
742 	data->category = rep->category;
743 	data->client_swapped = rep->clientSwapped;
744 	data->server_time = rep->serverTime;
745 	data->client_seq = rep->recordedSequenceNumber;
746 	/*
747 	 * compute the size of this protocol element.
748 	 */
749 	switch (rep->category) {
750 	case XRecordFromServer:
751 	    if (rep->elementHeader&XRecordFromServerTime) {
752 		if (current_index + 4 > rep->length << 2)
753 		    return Error;
754 		EXTRACT_CARD32(rep->clientSwapped,
755 			       reply->buf+current_index,
756 			       data->server_time);
757 		current_index += 4;
758 	    }
759 	    if (current_index + 1 > rep->length << 2)
760 		return Error;
761 	    switch (reply->buf[current_index]) {
762 	    case X_Reply: /* reply */
763 		if (current_index + 8 > rep->length << 2)
764 		    return Error;
765 		EXTRACT_CARD32(rep->clientSwapped,
766 			       reply->buf+current_index+4, datum_bytes);
767 		if (datum_bytes < 0 || datum_bytes > ((INT_MAX >> 2) - 8))
768 		    return Error;
769 		datum_bytes = (datum_bytes+8) << 2;
770 		break;
771 	    default: /* error or event */
772 		datum_bytes = 32;
773 	    }
774 	    break;
775 	case XRecordFromClient:
776 	    if (rep->elementHeader&XRecordFromClientTime) {
777 		if (current_index + 4 > rep->length << 2)
778 		    return Error;
779 		EXTRACT_CARD32(rep->clientSwapped,
780 			       reply->buf+current_index,
781 			       data->server_time);
782 		current_index += 4;
783 	    }
784 	    if (rep->elementHeader&XRecordFromClientSequence) {
785 		if (current_index + 4 > rep->length << 2)
786 		    return Error;
787 		EXTRACT_CARD32(rep->clientSwapped,
788 			       reply->buf+current_index,
789 			       data->client_seq);
790 		current_index += 4;
791 	    }
792 	    if (current_index + 4 > rep->length<<2)
793 		return Error;
794 	    if (reply->buf[current_index+2] == 0
795 		&& reply->buf[current_index+3] == 0) /* needn't swap 0 */
796 	    {	/* BIG-REQUESTS */
797 		if (current_index + 8 > rep->length << 2)
798 		    return Error;
799 		EXTRACT_CARD32(rep->clientSwapped,
800 			       reply->buf+current_index+4, datum_bytes);
801 	    } else {
802 		EXTRACT_CARD16(rep->clientSwapped,
803 			       reply->buf+current_index+2, datum_bytes);
804 	    }
805 	    if (datum_bytes < 0 || datum_bytes > INT_MAX >> 2)
806 		return Error;
807 	    datum_bytes <<= 2;
808 	    break;
809 	case XRecordClientStarted:
810 	    if (current_index + 8 > rep->length << 2)
811 		return Error;
812 	    EXTRACT_CARD16(rep->clientSwapped,
813 			   reply->buf+current_index+6, datum_bytes);
814 	    datum_bytes = (datum_bytes+2) << 2;
815 	    break;
816 	case XRecordClientDied:
817 	    if (rep->elementHeader&XRecordFromClientSequence) {
818 		if (current_index + 4 > rep->length << 2)
819 		    return Error;
820 		EXTRACT_CARD32(rep->clientSwapped,
821 			       reply->buf+current_index,
822 			       data->client_seq);
823 		current_index += 4;
824 	    } else if (current_index < rep->length << 2)
825 		return Error;
826 	    datum_bytes = 0;
827 	    break;
828 	case XRecordStartOfData:
829 	case XRecordEndOfData:
830 	    if (current_index < rep->length << 2)
831 		return Error;
832 	    datum_bytes = 0;
833 	    break;
834 	}
835 
836 	if (datum_bytes > 0) {
837 	    if (INT_MAX - datum_bytes < (rep->length << 2) - current_index) {
838 		fprintf(stderr,
839 			"XRecord: %lu-byte reply claims %d-byte element (seq %lu)\n",
840 			(unsigned long)rep->length << 2, current_index + datum_bytes,
841 			dpy->last_request_read);
842 		return Error;
843 	    }
844 	    /*
845 	     * This assignment (and indeed the whole buffer sharing
846 	     * scheme) assumes arbitrary 4-byte boundaries are
847 	     * addressable.
848 	     */
849 	    data->data = reply->buf+current_index;
850 	    reply->ref_count++;
851 	} else {
852 	    data->data = NULL;
853 	}
854 	data->data_len = datum_bytes >> 2;
855 
856 	(*callback)(closure, data);
857 
858 	current_index += datum_bytes;
859     } while (current_index<rep->length<<2);
860 
861     if (rep->category == XRecordEndOfData)
862 	return End;
863 
864     return Continue;
865 }
866 
867 Status
XRecordEnableContext(Display * dpy,XRecordContext context,XRecordInterceptProc callback,XPointer closure)868 XRecordEnableContext(Display *dpy, XRecordContext context,
869 		     XRecordInterceptProc callback, XPointer closure)
870 {
871     XExtDisplayInfo *info = find_display (dpy);
872     register xRecordEnableContextReq   	*req;
873     xRecordEnableContextReply 	rep;
874     struct reply_buffer *reply;
875     enum parser_return status;
876 
877     XRecordCheckExtension (dpy, info, 0);
878     LockDisplay(dpy);
879     GetReq(RecordEnableContext, req);
880 
881     req->reqType = info->codes->major_opcode;
882     req->recordReqType = X_RecordEnableContext;
883     req->context = context;
884 
885     while (1)
886     {
887 	/* This code should match that in XRecordEnableContextAsync */
888 	if (!_XReply (dpy, (xReply *)&rep, 0, xFalse))
889 	{
890 	    UnlockDisplay(dpy);
891 	    SyncHandle();
892 	    return 0;
893 	}
894 
895 	if (rep.length > INT_MAX >> 2) {
896 	    UnlockDisplay(dpy);
897 	    SyncHandle();
898 	    return 0;
899 	}
900 
901 	if (rep.length > 0) {
902 	    reply = alloc_reply_buffer(info, rep.length<<2);
903 	    if (!reply) {
904 		UnlockDisplay(dpy);
905 		SyncHandle();
906 		return 0;
907 	    }
908 	    _XRead (dpy, (char *)reply->buf, rep.length<<2);
909 	} else {
910 	    reply = NULL;
911 	}
912 
913 	status = parse_reply_call_callback(dpy, info, &rep, reply,
914 					   callback, closure);
915 	switch (status) {
916 	case Continue:
917 	    break;
918 	case End:
919 	    UnlockDisplay(dpy);
920 	    SyncHandle();
921 	    return 1;
922 	case Error:
923 	    UnlockDisplay(dpy);
924 	    SyncHandle();
925 	    return 0;
926 	}
927     }
928 }
929 
930 
931 typedef struct _record_async_state
932 {
933     unsigned long enable_seq;
934     _XAsyncHandler *async;
935     _XAsyncErrorState *error_state;
936     XExtDisplayInfo *info;
937     XRecordInterceptProc callback;
938     XPointer closure;
939 } record_async_state;
940 
941 static Bool
record_async_handler(register Display * dpy,register xReply * rep,char * buf,int len,XPointer adata)942 record_async_handler(
943     register Display *dpy,
944     register xReply *rep,
945     char *buf,
946     int len,
947     XPointer adata)
948 {
949     register record_async_state *state = (record_async_state *)adata;
950     struct reply_buffer *reply;
951     enum parser_return status;
952 
953     if (dpy->last_request_read != state->enable_seq)
954     {
955 	if (dpy->last_request_read > state->enable_seq) {
956 	    /* it is an error that we are still on the handler list */
957 	    fprintf(stderr, "XRecord: handler for seq %lu never saw XRecordEndOfData.  (seq now %lu)\n",
958 		    state->enable_seq, dpy->last_request_read);
959 	    DeqAsyncHandler(dpy, state->async);
960 	    Xfree(state->async);
961 	}
962 	return False;
963     }
964     if (rep->generic.type == X_Error)
965     {
966 	DeqAsyncHandler(dpy, state->async);
967 	Xfree(state->async);
968 	return False;
969     }
970 
971     if (rep->generic.length > 0) {
972 	reply = alloc_reply_buffer(state->info, rep->generic.length<<2);
973 
974 	if (!reply) {
975 	    DeqAsyncHandler(dpy, state->async);
976 	    Xfree(state->async);
977 	    return False;
978 	}
979 
980 	_XGetAsyncData(dpy, (char *)reply->buf, buf, len,
981 		       SIZEOF(xRecordEnableContextReply),
982 		       rep->generic.length << 2, 0);
983     } else {
984 	reply = NULL;
985     }
986 
987     status = parse_reply_call_callback(dpy, state->info,
988 				       (xRecordEnableContextReply*) rep,
989 				       reply, state->callback, state->closure);
990 
991     if (status != Continue)
992     {
993 	DeqAsyncHandler(dpy, state->async);
994 	Xfree(state->async);
995 	if (status == Error)
996 	    return False;
997     }
998 
999     return True;
1000 }
1001 
1002 /*
1003  * reads the first reply, StartOfData, synchronously,
1004  * then returns allowing the app to call XRecordProcessReplies
1005  * to get the rest.
1006  */
1007 Status
XRecordEnableContextAsync(Display * dpy,XRecordContext context,XRecordInterceptProc callback,XPointer closure)1008 XRecordEnableContextAsync(Display *dpy, XRecordContext context,
1009 			  XRecordInterceptProc callback, XPointer closure)
1010 {
1011     XExtDisplayInfo *info = find_display (dpy);
1012     register xRecordEnableContextReq *req;
1013     xRecordEnableContextReply rep;
1014     struct reply_buffer *reply;
1015     enum parser_return status;
1016     _XAsyncHandler *async;
1017     record_async_state *async_state;
1018 
1019     XRecordCheckExtension (dpy, info, 0);
1020     async = (_XAsyncHandler *)Xmalloc(sizeof(_XAsyncHandler) +
1021 	sizeof(record_async_state));
1022     if (!async)
1023 	return 0;
1024     async_state = (record_async_state *)(async + 1);
1025 
1026     LockDisplay(dpy);
1027     GetReq(RecordEnableContext, req);
1028 
1029     req->reqType = info->codes->major_opcode;
1030     req->recordReqType = X_RecordEnableContext;
1031     req->context = context;
1032 
1033     /* Get the StartOfData reply. */
1034     /* This code should match that in XRecordEnableContext */
1035     if (!_XReply (dpy, (xReply *)&rep, 0, xFalse))
1036     {
1037 	UnlockDisplay(dpy);
1038 	SyncHandle();
1039 	Xfree(async);
1040 	return 0;
1041     }
1042 
1043     /* this had better be a StartOfData, which has no extra data. */
1044     if (rep.length != 0) {
1045 	fprintf(stderr, "XRecord: malformed StartOfData for sequence %lu\n",
1046 		dpy->last_request_read);
1047     }
1048     reply = NULL;
1049 
1050     status = parse_reply_call_callback(dpy, info, &rep, reply,
1051 				       callback, closure);
1052     if (status != Continue)
1053     {
1054 	UnlockDisplay(dpy);
1055 	Xfree(async);
1056 	return 0;
1057     }
1058 
1059     /* hook in the async handler for the rest of the replies */
1060     async_state->enable_seq = dpy->request;
1061     async_state->async = async;
1062     async_state->info = info;
1063     async_state->callback = callback;
1064     async_state->closure = closure;
1065 
1066     async->next = dpy->async_handlers;
1067     async->handler = record_async_handler;
1068     async->data = (XPointer)async_state;
1069     dpy->async_handlers = async;
1070 
1071     UnlockDisplay(dpy);
1072     /* Don't invoke SyncHandle here, since this is an async
1073        function.  Does this break XSetAfterFunction() ? */
1074     return 1;
1075 }
1076 
1077 void
XRecordProcessReplies(Display * dpy)1078 XRecordProcessReplies(Display *dpy)
1079 {
1080     (void) XPending(dpy);
1081 }
1082 
1083 Status
XRecordDisableContext(Display * dpy,XRecordContext context)1084 XRecordDisableContext(Display *dpy, XRecordContext context)
1085 {
1086     XExtDisplayInfo *info = find_display (dpy);
1087     register xRecordDisableContextReq 	*req;
1088 
1089     XRecordCheckExtension (dpy, info, 0);
1090     LockDisplay(dpy);
1091     GetReq(RecordDisableContext, req);
1092     req->reqType = info->codes->major_opcode;
1093     req->recordReqType = X_RecordDisableContext;
1094     req->context =  context;
1095 
1096     UnlockDisplay(dpy);
1097     SyncHandle();
1098     return 1;
1099 }
1100 
1101 Status
XRecordFreeContext(Display * dpy,XRecordContext context)1102 XRecordFreeContext(Display *dpy, XRecordContext context)
1103 {
1104     XExtDisplayInfo *info = find_display (dpy);
1105     register xRecordFreeContextReq 	*req;
1106 
1107     XRecordCheckExtension (dpy, info, 0);
1108 
1109     LockDisplay(dpy);
1110     GetReq(RecordFreeContext, req);
1111     req->reqType = info->codes->major_opcode;
1112     req->recordReqType = X_RecordFreeContext;
1113     req->context = context;
1114 
1115     UnlockDisplay(dpy);
1116     SyncHandle();
1117     return 1;
1118 }
1119