rrproperty.c revision d9252ffb
1/*
2 * Copyright © 2006 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include "randrstr.h"
24#include "propertyst.h"
25#include "swaprep.h"
26#include <X11/Xatom.h>
27
28static int
29DeliverPropertyEvent(WindowPtr pWin, void *value)
30{
31    xRROutputPropertyNotifyEvent *event = value;
32    RREventPtr *pHead, pRREvent;
33
34    dixLookupResourceByType((void **) &pHead, pWin->drawable.id,
35                            RREventType, serverClient, DixReadAccess);
36    if (!pHead)
37        return WT_WALKCHILDREN;
38
39    for (pRREvent = *pHead; pRREvent; pRREvent = pRREvent->next) {
40        if (!(pRREvent->mask & RROutputPropertyNotifyMask))
41            continue;
42
43        event->window = pRREvent->window->drawable.id;
44        WriteEventsToClient(pRREvent->client, 1, (xEvent *) event);
45    }
46
47    return WT_WALKCHILDREN;
48}
49
50static void
51RRDeliverPropertyEvent(ScreenPtr pScreen, xEvent *event)
52{
53    if (!(dispatchException & (DE_RESET | DE_TERMINATE)))
54        WalkTree(pScreen, DeliverPropertyEvent, event);
55}
56
57static void
58RRDestroyOutputProperty(RRPropertyPtr prop)
59{
60    free(prop->valid_values);
61    free(prop->current.data);
62    free(prop->pending.data);
63    free(prop);
64}
65
66static void
67RRDeleteProperty(RROutputRec * output, RRPropertyRec * prop)
68{
69    xRROutputPropertyNotifyEvent event = {
70        .type = RREventBase + RRNotify,
71        .subCode = RRNotify_OutputProperty,
72        .output = output->id,
73        .state = PropertyDelete,
74        .atom = prop->propertyName,
75        .timestamp = currentTime.milliseconds
76    };
77
78    RRDeliverPropertyEvent(output->pScreen, (xEvent *) &event);
79
80    RRDestroyOutputProperty(prop);
81}
82
83void
84RRDeleteAllOutputProperties(RROutputPtr output)
85{
86    RRPropertyPtr prop, next;
87
88    for (prop = output->properties; prop; prop = next) {
89        next = prop->next;
90        RRDeleteProperty(output, prop);
91    }
92}
93
94static void
95RRInitOutputPropertyValue(RRPropertyValuePtr property_value)
96{
97    property_value->type = None;
98    property_value->format = 0;
99    property_value->size = 0;
100    property_value->data = NULL;
101}
102
103static RRPropertyPtr
104RRCreateOutputProperty(Atom property)
105{
106    RRPropertyPtr prop;
107
108    prop = (RRPropertyPtr) malloc(sizeof(RRPropertyRec));
109    if (!prop)
110        return NULL;
111    prop->next = NULL;
112    prop->propertyName = property;
113    prop->is_pending = FALSE;
114    prop->range = FALSE;
115    prop->immutable = FALSE;
116    prop->num_valid = 0;
117    prop->valid_values = NULL;
118    RRInitOutputPropertyValue(&prop->current);
119    RRInitOutputPropertyValue(&prop->pending);
120    return prop;
121}
122
123void
124RRDeleteOutputProperty(RROutputPtr output, Atom property)
125{
126    RRPropertyRec *prop, **prev;
127
128    for (prev = &output->properties; (prop = *prev); prev = &(prop->next))
129        if (prop->propertyName == property) {
130            *prev = prop->next;
131            RRDeleteProperty(output, prop);
132            return;
133        }
134}
135
136static void
137RRNoticePropertyChange(RROutputPtr output, Atom property, RRPropertyValuePtr value)
138{
139    const char *non_desktop_str = RR_PROPERTY_NON_DESKTOP;
140    Atom non_desktop_prop = MakeAtom(non_desktop_str, strlen(non_desktop_str), FALSE);
141
142    if (property == non_desktop_prop) {
143        if (value->type == XA_INTEGER && value->format == 32 && value->size >= 1) {
144            uint32_t     nonDesktopData;
145            Bool        nonDesktop;
146
147            memcpy(&nonDesktopData, value->data, sizeof (nonDesktopData));
148            nonDesktop = nonDesktopData != 0;
149
150            if (nonDesktop != output->nonDesktop) {
151                output->nonDesktop = nonDesktop;
152                RROutputChanged(output, 0);
153                RRTellChanged(output->pScreen);
154            }
155        }
156    }
157}
158
159int
160RRChangeOutputProperty(RROutputPtr output, Atom property, Atom type,
161                       int format, int mode, unsigned long len,
162                       const void *value, Bool sendevent, Bool pending)
163{
164    RRPropertyPtr prop;
165    rrScrPrivPtr pScrPriv = rrGetScrPriv(output->pScreen);
166    int size_in_bytes;
167    unsigned long total_len;
168    RRPropertyValuePtr prop_value;
169    RRPropertyValueRec new_value;
170    Bool add = FALSE;
171
172    size_in_bytes = format >> 3;
173
174    /* first see if property already exists */
175    prop = RRQueryOutputProperty(output, property);
176    if (!prop) {                /* just add to list */
177        prop = RRCreateOutputProperty(property);
178        if (!prop)
179            return BadAlloc;
180        add = TRUE;
181        mode = PropModeReplace;
182    }
183    if (pending && prop->is_pending)
184        prop_value = &prop->pending;
185    else
186        prop_value = &prop->current;
187
188    /* To append or prepend to a property the request format and type
189       must match those of the already defined property.  The
190       existing format and type are irrelevant when using the mode
191       "PropModeReplace" since they will be written over. */
192
193    if ((format != prop_value->format) && (mode != PropModeReplace))
194        return BadMatch;
195    if ((prop_value->type != type) && (mode != PropModeReplace))
196        return BadMatch;
197    new_value = *prop_value;
198    if (mode == PropModeReplace)
199        total_len = len;
200    else
201        total_len = prop_value->size + len;
202
203    if (mode == PropModeReplace || len > 0) {
204        void *new_data = NULL, *old_data = NULL;
205
206        new_value.data = xallocarray(total_len, size_in_bytes);
207        if (!new_value.data && total_len && size_in_bytes) {
208            if (add)
209                RRDestroyOutputProperty(prop);
210            return BadAlloc;
211        }
212        new_value.size = total_len;
213        new_value.type = type;
214        new_value.format = format;
215
216        switch (mode) {
217        case PropModeReplace:
218            new_data = new_value.data;
219            old_data = NULL;
220            break;
221        case PropModeAppend:
222            new_data = (void *) (((char *) new_value.data) +
223                                  (prop_value->size * size_in_bytes));
224            old_data = new_value.data;
225            break;
226        case PropModePrepend:
227            new_data = new_value.data;
228            old_data = (void *) (((char *) new_value.data) +
229                                  (len * size_in_bytes));
230            break;
231        }
232        if (new_data)
233            memcpy((char *) new_data, (char *) value, len * size_in_bytes);
234        if (old_data)
235            memcpy((char *) old_data, (char *) prop_value->data,
236                   prop_value->size * size_in_bytes);
237
238        if (pending && pScrPriv->rrOutputSetProperty &&
239            !pScrPriv->rrOutputSetProperty(output->pScreen, output,
240                                           prop->propertyName, &new_value)) {
241            free(new_value.data);
242            if (add)
243                RRDestroyOutputProperty(prop);
244            return BadValue;
245        }
246        free(prop_value->data);
247        *prop_value = new_value;
248    }
249
250    else if (len == 0) {
251        /* do nothing */
252    }
253
254    if (add) {
255        prop->next = output->properties;
256        output->properties = prop;
257    }
258
259    if (pending && prop->is_pending)
260        output->pendingProperties = TRUE;
261
262    if (!(pending && prop->is_pending))
263        RRNoticePropertyChange(output, prop->propertyName, prop_value);
264
265    if (sendevent) {
266        xRROutputPropertyNotifyEvent event = {
267            .type = RREventBase + RRNotify,
268            .subCode = RRNotify_OutputProperty,
269            .output = output->id,
270            .state = PropertyNewValue,
271            .atom = prop->propertyName,
272            .timestamp = currentTime.milliseconds
273        };
274        RRDeliverPropertyEvent(output->pScreen, (xEvent *) &event);
275    }
276    return Success;
277}
278
279Bool
280RRPostPendingProperties(RROutputPtr output)
281{
282    RRPropertyValuePtr pending_value;
283    RRPropertyValuePtr current_value;
284    RRPropertyPtr property;
285    Bool ret = TRUE;
286
287    if (!output->pendingProperties)
288        return TRUE;
289
290    output->pendingProperties = FALSE;
291    for (property = output->properties; property; property = property->next) {
292        /* Skip non-pending properties */
293        if (!property->is_pending)
294            continue;
295
296        pending_value = &property->pending;
297        current_value = &property->current;
298
299        /*
300         * If the pending and current values are equal, don't mark it
301         * as changed (which would deliver an event)
302         */
303        if (pending_value->type == current_value->type &&
304            pending_value->format == current_value->format &&
305            pending_value->size == current_value->size &&
306            !memcmp(pending_value->data, current_value->data,
307                    pending_value->size * (pending_value->format / 8)))
308            continue;
309
310        if (RRChangeOutputProperty(output, property->propertyName,
311                                   pending_value->type, pending_value->format,
312                                   PropModeReplace, pending_value->size,
313                                   pending_value->data, TRUE, FALSE) != Success)
314            ret = FALSE;
315    }
316    return ret;
317}
318
319RRPropertyPtr
320RRQueryOutputProperty(RROutputPtr output, Atom property)
321{
322    RRPropertyPtr prop;
323
324    for (prop = output->properties; prop; prop = prop->next)
325        if (prop->propertyName == property)
326            return prop;
327    return NULL;
328}
329
330RRPropertyValuePtr
331RRGetOutputProperty(RROutputPtr output, Atom property, Bool pending)
332{
333    RRPropertyPtr prop = RRQueryOutputProperty(output, property);
334    rrScrPrivPtr pScrPriv = rrGetScrPriv(output->pScreen);
335
336    if (!prop)
337        return NULL;
338    if (pending && prop->is_pending)
339        return &prop->pending;
340    else {
341#if RANDR_13_INTERFACE
342        /* If we can, try to update the property value first */
343        if (pScrPriv->rrOutputGetProperty)
344            pScrPriv->rrOutputGetProperty(output->pScreen, output,
345                                          prop->propertyName);
346#endif
347        return &prop->current;
348    }
349}
350
351int
352RRConfigureOutputProperty(RROutputPtr output, Atom property,
353                          Bool pending, Bool range, Bool immutable,
354                          int num_values, const INT32 *values)
355{
356    RRPropertyPtr prop = RRQueryOutputProperty(output, property);
357    Bool add = FALSE;
358    INT32 *new_values;
359
360    if (!prop) {
361        prop = RRCreateOutputProperty(property);
362        if (!prop)
363            return BadAlloc;
364        add = TRUE;
365    }
366    else if (prop->immutable && !immutable)
367        return BadAccess;
368
369    /*
370     * ranges must have even number of values
371     */
372    if (range && (num_values & 1)) {
373        if (add)
374            RRDestroyOutputProperty(prop);
375        return BadMatch;
376    }
377
378    new_values = xallocarray(num_values, sizeof(INT32));
379    if (!new_values && num_values) {
380        if (add)
381            RRDestroyOutputProperty(prop);
382        return BadAlloc;
383    }
384    if (num_values)
385        memcpy(new_values, values, num_values * sizeof(INT32));
386
387    /*
388     * Property moving from pending to non-pending
389     * loses any pending values
390     */
391    if (prop->is_pending && !pending) {
392        free(prop->pending.data);
393        RRInitOutputPropertyValue(&prop->pending);
394    }
395
396    prop->is_pending = pending;
397    prop->range = range;
398    prop->immutable = immutable;
399    prop->num_valid = num_values;
400    free(prop->valid_values);
401    prop->valid_values = new_values;
402
403    if (add) {
404        prop->next = output->properties;
405        output->properties = prop;
406    }
407
408    return Success;
409}
410
411int
412ProcRRListOutputProperties(ClientPtr client)
413{
414    REQUEST(xRRListOutputPropertiesReq);
415    Atom *pAtoms = NULL;
416    xRRListOutputPropertiesReply rep;
417    int numProps = 0;
418    RROutputPtr output;
419    RRPropertyPtr prop;
420
421    REQUEST_SIZE_MATCH(xRRListOutputPropertiesReq);
422
423    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
424
425    for (prop = output->properties; prop; prop = prop->next)
426        numProps++;
427    if (numProps)
428        if (!(pAtoms = xallocarray(numProps, sizeof(Atom))))
429            return BadAlloc;
430
431    rep = (xRRListOutputPropertiesReply) {
432        .type = X_Reply,
433        .sequenceNumber = client->sequence,
434        .length = bytes_to_int32(numProps * sizeof(Atom)),
435        .nAtoms = numProps
436    };
437    if (client->swapped) {
438        swaps(&rep.sequenceNumber);
439        swapl(&rep.length);
440        swaps(&rep.nAtoms);
441    }
442    WriteToClient(client, sizeof(xRRListOutputPropertiesReply), &rep);
443
444    if (numProps) {
445        /* Copy property name atoms to reply buffer */
446        Atom *temppAtoms = pAtoms;
447        for (prop = output->properties; prop; prop = prop->next)
448            *temppAtoms++ = prop->propertyName;
449
450        client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write;
451        WriteSwappedDataToClient(client, numProps * sizeof(Atom), pAtoms);
452        free(pAtoms);
453    }
454    return Success;
455}
456
457int
458ProcRRQueryOutputProperty(ClientPtr client)
459{
460    REQUEST(xRRQueryOutputPropertyReq);
461    xRRQueryOutputPropertyReply rep;
462    RROutputPtr output;
463    RRPropertyPtr prop;
464    char *extra = NULL;
465
466    REQUEST_SIZE_MATCH(xRRQueryOutputPropertyReq);
467
468    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
469
470    prop = RRQueryOutputProperty(output, stuff->property);
471    if (!prop)
472        return BadName;
473
474    if (prop->num_valid) {
475        extra = xallocarray(prop->num_valid, sizeof(INT32));
476        if (!extra)
477            return BadAlloc;
478    }
479
480    rep = (xRRQueryOutputPropertyReply) {
481        .type = X_Reply,
482        .sequenceNumber = client->sequence,
483        .length = prop->num_valid,
484        .pending = prop->is_pending,
485        .range = prop->range,
486        .immutable = prop->immutable
487    };
488
489    if (client->swapped) {
490        swaps(&rep.sequenceNumber);
491        swapl(&rep.length);
492    }
493    WriteToClient(client, sizeof(xRRQueryOutputPropertyReply), &rep);
494    if (prop->num_valid) {
495        memcpy(extra, prop->valid_values, prop->num_valid * sizeof(INT32));
496        client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write;
497        WriteSwappedDataToClient(client, prop->num_valid * sizeof(INT32),
498                                 extra);
499        free(extra);
500    }
501    return Success;
502}
503
504int
505ProcRRConfigureOutputProperty(ClientPtr client)
506{
507    REQUEST(xRRConfigureOutputPropertyReq);
508    RROutputPtr output;
509    int num_valid;
510
511    REQUEST_AT_LEAST_SIZE(xRRConfigureOutputPropertyReq);
512
513    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
514
515    if (RROutputIsLeased(output))
516        return BadAccess;
517
518    num_valid =
519        stuff->length - bytes_to_int32(sizeof(xRRConfigureOutputPropertyReq));
520    return RRConfigureOutputProperty(output, stuff->property, stuff->pending,
521                                     stuff->range, FALSE, num_valid,
522                                     (INT32 *) (stuff + 1));
523}
524
525int
526ProcRRChangeOutputProperty(ClientPtr client)
527{
528    REQUEST(xRRChangeOutputPropertyReq);
529    RROutputPtr output;
530    char format, mode;
531    unsigned long len;
532    int sizeInBytes;
533    uint64_t totalSize;
534    int err;
535
536    REQUEST_AT_LEAST_SIZE(xRRChangeOutputPropertyReq);
537    UpdateCurrentTime();
538    format = stuff->format;
539    mode = stuff->mode;
540    if ((mode != PropModeReplace) && (mode != PropModeAppend) &&
541        (mode != PropModePrepend)) {
542        client->errorValue = mode;
543        return BadValue;
544    }
545    if ((format != 8) && (format != 16) && (format != 32)) {
546        client->errorValue = format;
547        return BadValue;
548    }
549    len = stuff->nUnits;
550    if (len > bytes_to_int32((0xffffffff - sizeof(xChangePropertyReq))))
551        return BadLength;
552    sizeInBytes = format >> 3;
553    totalSize = len * sizeInBytes;
554    REQUEST_FIXED_SIZE(xRRChangeOutputPropertyReq, totalSize);
555
556    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
557
558    if (!ValidAtom(stuff->property)) {
559        client->errorValue = stuff->property;
560        return BadAtom;
561    }
562    if (!ValidAtom(stuff->type)) {
563        client->errorValue = stuff->type;
564        return BadAtom;
565    }
566
567    err = RRChangeOutputProperty(output, stuff->property,
568                                 stuff->type, (int) format,
569                                 (int) mode, len, (void *) &stuff[1], TRUE,
570                                 TRUE);
571    if (err != Success)
572        return err;
573    else
574        return Success;
575}
576
577int
578ProcRRDeleteOutputProperty(ClientPtr client)
579{
580    REQUEST(xRRDeleteOutputPropertyReq);
581    RROutputPtr output;
582    RRPropertyPtr prop;
583
584    REQUEST_SIZE_MATCH(xRRDeleteOutputPropertyReq);
585    UpdateCurrentTime();
586    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
587
588    if (RROutputIsLeased(output))
589        return BadAccess;
590
591    if (!ValidAtom(stuff->property)) {
592        client->errorValue = stuff->property;
593        return BadAtom;
594    }
595
596    prop = RRQueryOutputProperty(output, stuff->property);
597    if (!prop) {
598        client->errorValue = stuff->property;
599        return BadName;
600    }
601
602    if (prop->immutable) {
603        client->errorValue = stuff->property;
604        return BadAccess;
605    }
606
607    RRDeleteOutputProperty(output, stuff->property);
608    return Success;
609}
610
611int
612ProcRRGetOutputProperty(ClientPtr client)
613{
614    REQUEST(xRRGetOutputPropertyReq);
615    RRPropertyPtr prop, *prev;
616    RRPropertyValuePtr prop_value;
617    unsigned long n, len, ind;
618    RROutputPtr output;
619    xRRGetOutputPropertyReply reply;
620    char *extra = NULL;
621
622    REQUEST_SIZE_MATCH(xRRGetOutputPropertyReq);
623    if (stuff->delete)
624        UpdateCurrentTime();
625    VERIFY_RR_OUTPUT(stuff->output, output,
626                     stuff->delete ? DixWriteAccess : DixReadAccess);
627
628    if (!ValidAtom(stuff->property)) {
629        client->errorValue = stuff->property;
630        return BadAtom;
631    }
632    if ((stuff->delete != xTrue) && (stuff->delete != xFalse)) {
633        client->errorValue = stuff->delete;
634        return BadValue;
635    }
636    if ((stuff->type != AnyPropertyType) && !ValidAtom(stuff->type)) {
637        client->errorValue = stuff->type;
638        return BadAtom;
639    }
640
641    for (prev = &output->properties; (prop = *prev); prev = &prop->next)
642        if (prop->propertyName == stuff->property)
643            break;
644
645    reply = (xRRGetOutputPropertyReply) {
646        .type = X_Reply,
647        .sequenceNumber = client->sequence
648    };
649    if (!prop) {
650        reply.nItems = 0;
651        reply.length = 0;
652        reply.bytesAfter = 0;
653        reply.propertyType = None;
654        reply.format = 0;
655        if (client->swapped) {
656            swaps(&reply.sequenceNumber);
657            swapl(&reply.length);
658            swapl(&reply.propertyType);
659            swapl(&reply.bytesAfter);
660            swapl(&reply.nItems);
661        }
662        WriteToClient(client, sizeof(xRRGetOutputPropertyReply), &reply);
663        return Success;
664    }
665
666    if (prop->immutable && stuff->delete)
667        return BadAccess;
668
669    prop_value = RRGetOutputProperty(output, stuff->property, stuff->pending);
670    if (!prop_value)
671        return BadAtom;
672
673    /* If the request type and actual type don't match. Return the
674       property information, but not the data. */
675
676    if (((stuff->type != prop_value->type) && (stuff->type != AnyPropertyType))
677        ) {
678        reply.bytesAfter = prop_value->size;
679        reply.format = prop_value->format;
680        reply.length = 0;
681        reply.nItems = 0;
682        reply.propertyType = prop_value->type;
683        if (client->swapped) {
684            swaps(&reply.sequenceNumber);
685            swapl(&reply.length);
686            swapl(&reply.propertyType);
687            swapl(&reply.bytesAfter);
688            swapl(&reply.nItems);
689        }
690        WriteToClient(client, sizeof(xRRGetOutputPropertyReply), &reply);
691        return Success;
692    }
693
694/*
695 *  Return type, format, value to client
696 */
697    n = (prop_value->format / 8) * prop_value->size;    /* size (bytes) of prop */
698    ind = stuff->longOffset << 2;
699
700    /* If longOffset is invalid such that it causes "len" to
701       be negative, it's a value error. */
702
703    if (n < ind) {
704        client->errorValue = stuff->longOffset;
705        return BadValue;
706    }
707
708    len = min(n - ind, 4 * stuff->longLength);
709
710    if (len) {
711        extra = malloc(len);
712        if (!extra)
713            return BadAlloc;
714    }
715    reply.bytesAfter = n - (ind + len);
716    reply.format = prop_value->format;
717    reply.length = bytes_to_int32(len);
718    if (prop_value->format)
719        reply.nItems = len / (prop_value->format / 8);
720    else
721        reply.nItems = 0;
722    reply.propertyType = prop_value->type;
723
724    if (stuff->delete && (reply.bytesAfter == 0)) {
725        xRROutputPropertyNotifyEvent event = {
726            .type = RREventBase + RRNotify,
727            .subCode = RRNotify_OutputProperty,
728            .output = output->id,
729            .state = PropertyDelete,
730            .atom = prop->propertyName,
731            .timestamp = currentTime.milliseconds
732        };
733        RRDeliverPropertyEvent(output->pScreen, (xEvent *) &event);
734    }
735
736    if (client->swapped) {
737        swaps(&reply.sequenceNumber);
738        swapl(&reply.length);
739        swapl(&reply.propertyType);
740        swapl(&reply.bytesAfter);
741        swapl(&reply.nItems);
742    }
743    WriteToClient(client, sizeof(xGenericReply), &reply);
744    if (len) {
745        memcpy(extra, (char *) prop_value->data + ind, len);
746        switch (reply.format) {
747        case 32:
748            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap32Write;
749            break;
750        case 16:
751            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write;
752            break;
753        default:
754            client->pSwapReplyFunc = (ReplySwapPtr) WriteToClient;
755            break;
756        }
757        WriteSwappedDataToClient(client, len, extra);
758        free(extra);
759    }
760
761    if (stuff->delete && (reply.bytesAfter == 0)) {     /* delete the Property */
762        *prev = prop->next;
763        RRDestroyOutputProperty(prop);
764    }
765    return Success;
766}
767