Commit message: record: Fix out of bounds access in SwapCreateRegister()

ZDI-CAN-14952, CVE-2021-4011

This vulnerability was discovered and the fix was suggested by:
Jan-Niklas Sohn working with Trend Micro Zero Day Initiative

Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
CVE: None
CWE: CWE-787
Vulnerability description: None

Project: xorg-xserver
File: record.c

ProcRecordUnregisterClients(ClientPtr client)
{
    RecordContextPtr pContext;
    int err;

    REQUEST(xRecordUnregisterClientsReq);
    XID *pCanonClients;
    int nClients;
    int i;

    REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
    if (INT_MAX / 4 < stuff->nClients ||
        (client->req_len << 2) - SIZEOF(xRecordUnregisterClientsReq) !=
        4 * stuff->nClients)
        return BadLength;
    VERIFY_CONTEXT(pContext, stuff->context, client);
    err = RecordSanityCheckClientSpecifiers(client, (XID *) &stuff[1],
                                            stuff->nClients, 0);
    if (err != Success)
        return err;

    nClients = stuff->nClients;
    pCanonClients = RecordCanonicalizeClientSpecifiers((XID *) &stuff[1],
                                                       &nClients, 0);
    if (!pCanonClients)
        return BadAlloc;

    for (i = 0; i < nClients; i++) {
        RecordDeleteClientFromContext(pContext, pCanonClients[i]);
    }
    if (pCanonClients != (XID *) &stuff[1])
        free(pCanonClients);
    return Success;
}                               /* ProcRecordUnregisterClients */

Project: xorg-xserver
File: record.c

SProcRecordDispatch(ClientPtr client)
{
    REQUEST(xReq);

    switch (stuff->data) {
    case X_RecordQueryVersion:
        return SProcRecordQueryVersion(client);
    case X_RecordCreateContext:
        return SProcRecordCreateContext(client);
    case X_RecordRegisterClients:
        return SProcRecordRegisterClients(client);
    case X_RecordUnregisterClients:
        return SProcRecordUnregisterClients(client);
    case X_RecordGetContext:
        return SProcRecordGetContext(client);
    case X_RecordEnableContext:
        return SProcRecordEnableContext(client);
    case X_RecordDisableContext:
        return SProcRecordDisableContext(client);
    case X_RecordFreeContext:
        return SProcRecordFreeContext(client);
    default:
        return BadRequest;
    }
}                               /* SProcRecordDispatch */

Project: xorg-xserver
File: record.c

RecordFindClientOnContext(RecordContextPtr pContext,
                          XID clientspec, int *pposition)
{
    RecordClientsAndProtocolPtr pRCAP;

    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
        int i;

        for (i = 0; i < pRCAP->numClients; i++) {
            if (pRCAP->pClientIDs[i] == clientspec) {
                if (pposition)
                    *pposition = i;
                return pRCAP;
            }
        }
    }
    return NULL;
}                               /* RecordFindClientOnContext */

Project: xorg-xserver
File: record.c

ProcRecordRegisterClients(ClientPtr client)
{
    RecordContextPtr pContext;

    REQUEST(xRecordRegisterClientsReq);

    REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
    VERIFY_CONTEXT(pContext, stuff->context, client);

    return RecordRegisterClients(pContext, client, stuff);
}                               /* ProcRecordRegisterClients */

Project: xorg-xserver
File: record.c

SProcRecordFreeContext(ClientPtr client)
{
    REQUEST(xRecordFreeContextReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xRecordFreeContextReq);
    swapl(&stuff->context);
    return ProcRecordFreeContext(client);
}                               /* SProcRecordFreeContext */

Project: xorg-xserver
File: record.c

RecordExtensionInit(void)
{
    ExtensionEntry *extentry;

    RTContext = CreateNewResourceType(RecordDeleteContext, "RecordContext");
    if (!RTContext)
        return;

    if (!dixRegisterPrivateKey(RecordClientPrivateKey, PRIVATE_CLIENT, 0))
        return;

    ppAllContexts = NULL;
    numContexts = numEnabledContexts = numEnabledRCAPs = 0;

    if (!AddCallback(&ClientStateCallback, RecordAClientStateChange, NULL))
        return;

    extentry = AddExtension(RECORD_NAME, RecordNumEvents, RecordNumErrors,
                            ProcRecordDispatch, SProcRecordDispatch,
                            RecordCloseDown, StandardMinorOpcode);
    if (!extentry) {
        DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
        return;
    }
    SetResourceTypeErrorValue(RTContext,
                              extentry->errorBase + XRecordBadContext);

}                               /* RecordExtensionInit */

Project: xorg-xserver
File: record.c

SProcRecordRegisterClients(ClientPtr client)
{
    REQUEST(xRecordRegisterClientsReq);
    int status;

    swaps(&stuff->length);
    REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
    if ((status = SwapCreateRegister(client, (void *) stuff)) != Success)
        return status;
    return ProcRecordRegisterClients(client);
}                               /* SProcRecordRegisterClients */

Project: xorg-xserver
File: record.c

RecordConvertSetToRanges(RecordSetPtr pSet,
                         GetContextRangeInfoPtr pri,
                         int byteoffset,
                         Bool card8, unsigned int imax, int *pStartIndex)
{
    int nRanges;
    RecordSetIteratePtr pIter = NULL;
    RecordSetInterval interval;
    CARD8 *pCARD8;
    CARD16 *pCARD16;
    int err;

    if (!pSet)
        return Success;

    nRanges = pStartIndex ? *pStartIndex : 0;
    while ((pIter = RecordIterateSet(pSet, pIter, &interval))) {
        if (interval.first > imax)
            break;
        if (interval.last > imax)
            interval.last = imax;
        nRanges++;
        if (nRanges > pri->size) {
            err = RecordAllocRanges(pri, nRanges);
            if (err != Success)
                return err;
        }
        else
            pri->nRanges = max(pri->nRanges, nRanges);
        if (card8) {
            pCARD8 = ((CARD8 *) &pri->pRanges[nRanges - 1]) + byteoffset;
            *pCARD8++ = interval.first;
            *pCARD8 = interval.last;
        }
        else {
            pCARD16 = (CARD16 *)
                (((char *) &pri->pRanges[nRanges - 1]) + byteoffset);
            *pCARD16++ = interval.first;
            *pCARD16 = interval.last;
        }
    }
    if (pStartIndex)
        *pStartIndex = nRanges;
    return Success;
}                               /* RecordConvertSetToRanges */

Project: xorg-xserver
File: record.c

RecordDeleteClientFromContext(RecordContextPtr pContext, XID clientspec)
{
    RecordClientsAndProtocolPtr pRCAP;
    int position;

    if ((pRCAP = RecordFindClientOnContext(pContext, clientspec, &position)))
        RecordDeleteClientFromRCAP(pRCAP, position);
}                               /* RecordDeleteClientFromContext */

Project: xorg-xserver
File: record.c

RecordRegisterClients(RecordContextPtr pContext, ClientPtr client,
                      xRecordRegisterClientsReq * stuff)
{
    int err;
    int i;
    SetInfoPtr si;
    int maxSets;
    int nExtReqSets = 0;
    int nExtRepSets = 0;
    int extReqSetsOffset = 0;
    int extRepSetsOffset = 0;
    SetInfoPtr pExtReqSets, pExtRepSets;
    int clientListOffset;
    XID *pCanonClients;
    int clientStarted = 0, clientDied = 0;
    xRecordRange *pRanges, rr;
    int nClients;
    int sizeClients;
    int totRCAPsize;
    RecordClientsAndProtocolPtr pRCAP;
    int pad;
    XID recordingClient;

    /* do all sanity checking up front */

    err = RecordSanityCheckRegisterClients(pContext, client, stuff);
    if (err != Success)
        return err;

    /* if element headers changed, flush buffer */

    if (pContext->elemHeaders != stuff->elementHeader) {
        RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
        pContext->elemHeaders = stuff->elementHeader;
    }

    nClients = stuff->nClients;
    if (!nClients)
        /* if empty clients list, we're done. */
        return Success;

    recordingClient = pContext->pRecordingClient ?
        pContext->pRecordingClient->clientAsMask : 0;
    pCanonClients = RecordCanonicalizeClientSpecifiers((XID *) &stuff[1],
                                                       &nClients,
                                                       recordingClient);
    if (!pCanonClients)
        return BadAlloc;

    /* We may have to create as many as one set for each "predefined"
     * protocol types, plus one per range for extension requests, plus one per
     * range for extension replies.
     */
    maxSets = PREDEFSETS + 2 * stuff->nRanges;
    si = xallocarray(maxSets, sizeof(SetInfoRec));
    if (!si) {
        err = BadAlloc;
        goto bailout;
    }
    memset(si, 0, sizeof(SetInfoRec) * maxSets);

    /* theoretically you must do this because NULL may not be all-bits-zero */
    for (i = 0; i < maxSets; i++)
        si[i].intervals = NULL;

    pExtReqSets = si + PREDEFSETS;
    pExtRepSets = pExtReqSets + stuff->nRanges;

    pRanges = (xRecordRange *) (((XID *) &stuff[1]) + stuff->nClients);

    err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
                                         offset_of(rr, coreRequestsFirst), NULL,
                                         NULL);
    if (err != Success)
        goto bailout;

    err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
                                         offset_of(rr, extRequestsMajorFirst),
                                         pExtReqSets, &nExtReqSets);
    if (err != Success)
        goto bailout;

    err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
                                         offset_of(rr, coreRepliesFirst), NULL,
                                         NULL);
    if (err != Success)
        goto bailout;

    err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
                                         offset_of(rr, extRepliesMajorFirst),
                                         pExtRepSets, &nExtRepSets);
    if (err != Success)
        goto bailout;

    err = RecordConvertRangesToIntervals(&si[ERR], pRanges, stuff->nRanges,
                                         offset_of(rr, errorsFirst), NULL,
                                         NULL);
    if (err != Success)
        goto bailout;

    err = RecordConvertRangesToIntervals(&si[DLEV], pRanges, stuff->nRanges,
                                         offset_of(rr, deliveredEventsFirst),
                                         NULL, NULL);
    if (err != Success)
        goto bailout;

    err = RecordConvertRangesToIntervals(&si[DEV], pRanges, stuff->nRanges,
                                         offset_of(rr, deviceEventsFirst), NULL,
                                         NULL);
    if (err != Success)
        goto bailout;

    /* collect client-started and client-died */

    for (i = 0; i < stuff->nRanges; i++) {
        if (pRanges[i].clientStarted)
            clientStarted = TRUE;
        if (pRanges[i].clientDied)
            clientDied = TRUE;
    }

    /*  We now have all the information collected to create all the sets,
     * and we can compute the total memory required for the RCAP.
     */

    totRCAPsize = sizeof(RecordClientsAndProtocolRec);

    /* leave a little room to grow before forcing a separate allocation */
    sizeClients = nClients + CLIENT_ARRAY_GROWTH_INCREMENT;
    pad = RecordPadAlign(totRCAPsize, sizeof(XID));
    clientListOffset = totRCAPsize + pad;
    totRCAPsize += pad + sizeClients * sizeof(XID);

    if (nExtReqSets) {
        pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
        extReqSetsOffset = totRCAPsize + pad;
        totRCAPsize += pad + (nExtReqSets + 1) * sizeof(RecordMinorOpRec);
    }
    if (nExtRepSets) {
        pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
        extRepSetsOffset = totRCAPsize + pad;
        totRCAPsize += pad + (nExtRepSets + 1) * sizeof(RecordMinorOpRec);
    }

    for (i = 0; i < maxSets; i++) {
        if (si[i].nintervals) {
            si[i].size =
                RecordSetMemoryRequirements(si[i].intervals, si[i].nintervals,
                                            &si[i].align);
            pad = RecordPadAlign(totRCAPsize, si[i].align);
            si[i].offset = pad + totRCAPsize;
            totRCAPsize += pad + si[i].size;
        }
    }

    /* allocate memory for the whole RCAP */

    pRCAP = (RecordClientsAndProtocolPtr) malloc(totRCAPsize);
    if (!pRCAP) {
        err = BadAlloc;
        goto bailout;
    }

    /* fill in the RCAP */

    pRCAP->pContext = pContext;
    pRCAP->pClientIDs = (XID *) ((char *) pRCAP + clientListOffset);
    pRCAP->numClients = nClients;
    pRCAP->sizeClients = sizeClients;
    pRCAP->clientIDsSeparatelyAllocated = 0;
    for (i = 0; i < nClients; i++) {
        RecordDeleteClientFromContext(pContext, pCanonClients[i]);
        pRCAP->pClientIDs[i] = pCanonClients[i];
    }

    /* create all the sets */

    if (si[REQ].intervals) {
        pRCAP->pRequestMajorOpSet =
            RecordCreateSet(si[REQ].intervals, si[REQ].nintervals,
                            (RecordSetPtr) ((char *) pRCAP + si[REQ].offset),
                            si[REQ].size);
    }
    else
        pRCAP->pRequestMajorOpSet = NULL;

    if (si[REP].intervals) {
        pRCAP->pReplyMajorOpSet =
            RecordCreateSet(si[REP].intervals, si[REP].nintervals,
                            (RecordSetPtr) ((char *) pRCAP + si[REP].offset),
                            si[REP].size);
    }
    else
        pRCAP->pReplyMajorOpSet = NULL;

    if (si[ERR].intervals) {
        pRCAP->pErrorSet =
            RecordCreateSet(si[ERR].intervals, si[ERR].nintervals,
                            (RecordSetPtr) ((char *) pRCAP + si[ERR].offset),
                            si[ERR].size);
    }
    else
        pRCAP->pErrorSet = NULL;

    if (si[DEV].intervals) {
        pRCAP->pDeviceEventSet =
            RecordCreateSet(si[DEV].intervals, si[DEV].nintervals,
                            (RecordSetPtr) ((char *) pRCAP + si[DEV].offset),
                            si[DEV].size);
    }
    else
        pRCAP->pDeviceEventSet = NULL;

    if (si[DLEV].intervals) {
        pRCAP->pDeliveredEventSet =
            RecordCreateSet(si[DLEV].intervals, si[DLEV].nintervals,
                            (RecordSetPtr) ((char *) pRCAP + si[DLEV].offset),
                            si[DLEV].size);
    }
    else
        pRCAP->pDeliveredEventSet = NULL;

    if (nExtReqSets) {
        pRCAP->pRequestMinOpInfo = (RecordMinorOpPtr)
            ((char *) pRCAP + extReqSetsOffset);
        pRCAP->pRequestMinOpInfo[0].count = nExtReqSets;
        for (i = 0; i < nExtReqSets; i++, pExtReqSets++) {
            pRCAP->pRequestMinOpInfo[i + 1].major.first = pExtReqSets->first;
            pRCAP->pRequestMinOpInfo[i + 1].major.last = pExtReqSets->last;
            pRCAP->pRequestMinOpInfo[i + 1].major.pMinOpSet =
                RecordCreateSet(pExtReqSets->intervals,
                                pExtReqSets->nintervals,
                                (RecordSetPtr) ((char *) pRCAP +
                                                pExtReqSets->offset),
                                pExtReqSets->size);
        }
    }
    else
        pRCAP->pRequestMinOpInfo = NULL;

    if (nExtRepSets) {
        pRCAP->pReplyMinOpInfo = (RecordMinorOpPtr)
            ((char *) pRCAP + extRepSetsOffset);
        pRCAP->pReplyMinOpInfo[0].count = nExtRepSets;
        for (i = 0; i < nExtRepSets; i++, pExtRepSets++) {
            pRCAP->pReplyMinOpInfo[i + 1].major.first = pExtRepSets->first;
            pRCAP->pReplyMinOpInfo[i + 1].major.last = pExtRepSets->last;
            pRCAP->pReplyMinOpInfo[i + 1].major.pMinOpSet =
                RecordCreateSet(pExtRepSets->intervals,
                                pExtRepSets->nintervals,
                                (RecordSetPtr) ((char *) pRCAP +
                                                pExtRepSets->offset),
                                pExtRepSets->size);
        }
    }
    else
        pRCAP->pReplyMinOpInfo = NULL;

    pRCAP->clientStarted = clientStarted;
    pRCAP->clientDied = clientDied;

    /* link the RCAP onto the context */

    pRCAP->pNextRCAP = pContext->pListOfRCAP;
    pContext->pListOfRCAP = pRCAP;

    if (pContext->pRecordingClient)     /* context enabled */
        RecordInstallHooks(pRCAP, 0);

 bailout:
    if (si) {
        for (i = 0; i < maxSets; i++)
            free(si[i].intervals);
        free(si);
    }
    if (pCanonClients && pCanonClients != (XID *) &stuff[1])
        free(pCanonClients);
    return err;
}                               /* RecordRegisterClients */

Project: xorg-xserver
File: record.c

SProcRecordDisableContext(ClientPtr client)
{
    REQUEST(xRecordDisableContextReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xRecordDisableContextReq);
    swapl(&stuff->context);
    return ProcRecordDisableContext(client);
}                               /* SProcRecordDisableContext */

Project: xorg-xserver
File: record.c

RecordABigRequest(RecordContextPtr pContext, ClientPtr client, xReq * stuff)
{
    CARD32 bigLength;
    int bytesLeft;

    /* note: client->req_len has been frobbed by ReadRequestFromClient
     * (os/io.c) to discount the extra 4 bytes taken by the extended length
     * field in a big request.  The actual request length to record is
     * client->req_len + 1 (measured in CARD32s).
     */

    /* record the request header */
    bytesLeft = client->req_len << 2;
    RecordAProtocolElement(pContext, client, XRecordFromClient,
                           (void *) stuff, SIZEOF(xReq), 0, bytesLeft);

    /* reinsert the extended length field that was squished out */
    bigLength = client->req_len + bytes_to_int32(sizeof(bigLength));
    if (client->swapped)
        swapl(&bigLength);
    RecordAProtocolElement(pContext, client, XRecordFromClient,
                           (void *) &bigLength, sizeof(bigLength), 0,
                           /* continuation */ -1);
    bytesLeft -= sizeof(bigLength);

    /* record the rest of the request after the length */
    RecordAProtocolElement(pContext, client, XRecordFromClient,
                           (void *) (stuff + 1), bytesLeft, 0,
                           /* continuation */ -1);
}                               /* RecordABigRequest */

Project: xorg-xserver
File: record.c

RecordDisableContext(RecordContextPtr pContext)
{
    RecordClientsAndProtocolPtr pRCAP;
    int i;

    if (!pContext->pRecordingClient)
        return;
    if (!pContext->pRecordingClient->clientGone) {
        RecordAProtocolElement(pContext, NULL, XRecordEndOfData, NULL, 0, 0, 0);
        RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
    }
    /* Re-enable request processing on this connection. */
    AttendClient(pContext->pRecordingClient);

    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
        RecordUninstallHooks(pRCAP, 0);
    }

    pContext->pRecordingClient = NULL;

    /* move the newly disabled context to the rear part of ppAllContexts,
     * where all the disabled contexts are
     */
    i = RecordFindContextOnAllContexts(pContext);
    assert(i != -1);
    assert(i < numEnabledContexts);
    if (i != (numEnabledContexts - 1)) {
        ppAllContexts[i] = ppAllContexts[numEnabledContexts - 1];
        ppAllContexts[numEnabledContexts - 1] = pContext;
    }
    --numEnabledContexts;
    assert(numEnabledContexts >= 0);
}                               /* RecordDisableContext */

Project: xorg-xserver
File: record.c

SwapCreateRegister(ClientPtr client, xRecordRegisterClientsReq * stuff)
{
    int i;
    XID *pClientID;

    swapl(&stuff->context);
    swapl(&stuff->nClients);
    swapl(&stuff->nRanges);
    pClientID = (XID *) &stuff[1];
    if (stuff->nClients >
        client->req_len - bytes_to_int32(sz_xRecordRegisterClientsReq))
        return BadLength;
    for (i = 0; i < stuff->nClients; i++, pClientID++) {
        swapl(pClientID);
    }
    if (stuff->nRanges >
        (client->req_len - bytes_to_int32(sz_xRecordRegisterClientsReq)
        - stuff->nClients) / bytes_to_int32(sz_xRecordRange))
        return BadLength;
    RecordSwapRanges((xRecordRange *) pClientID, stuff->nRanges);
    return Success;
}                               /* SwapCreateRegister */

Project: xorg-xserver
File: record.c

RecordADeliveredEventOrError(CallbackListPtr *pcbl, void *nulldata,
                             void *calldata)
{
    EventInfoRec *pei = (EventInfoRec *) calldata;
    RecordContextPtr pContext;
    RecordClientsAndProtocolPtr pRCAP;
    int eci;                    /* enabled context index */
    ClientPtr pClient = pei->client;

    for (eci = 0; eci < numEnabledContexts; eci++) {
        pContext = ppAllContexts[eci];
        pRCAP = RecordFindClientOnContext(pContext, pClient->clientAsMask,
                                          NULL);
        if (pRCAP && (pRCAP->pDeliveredEventSet || pRCAP->pErrorSet)) {
            int ev;             /* event index */
            xEvent *pev = pei->events;

            for (ev = 0; ev < pei->count; ev++, pev++) {
                int recordit = 0;

                if (pRCAP->pErrorSet) {
                    recordit = RecordIsMemberOfSet(pRCAP->pErrorSet,
                                                   ((xError *) (pev))->
                                                   errorCode);
                }
                else if (pRCAP->pDeliveredEventSet) {
                    recordit = RecordIsMemberOfSet(pRCAP->pDeliveredEventSet,
                                                   pev->u.u.type & 0177);
                }
                if (recordit) {
                    xEvent swappedEvent;
                    xEvent *pEvToRecord = pev;

                    if (pClient->swapped) {
                        (*EventSwapVector[pev->u.u.type & 0177])
                            (pev, &swappedEvent);
                        pEvToRecord = &swappedEvent;

                    }
                    RecordAProtocolElement(pContext, pClient,
                                           XRecordFromServer, pEvToRecord,
                                           SIZEOF(xEvent), 0, 0);
                }
            }                   /* end for each event */
        }                       /* end this client is on this context */
    }                           /* end for each enabled context */
}                               /* RecordADeliveredEventOrError */

Project: xorg-xserver
File: record.c

RecordAllocRanges(GetContextRangeInfoPtr pri, int nRanges)
{
    int newsize;
    xRecordRange *pNewRange;

#define SZINCR 8

    newsize = max(pri->size + SZINCR, nRanges);
    pNewRange = reallocarray(pri->pRanges, newsize, sizeof(xRecordRange));
    if (!pNewRange)
        return BadAlloc;

    pri->pRanges = pNewRange;
    pri->size = newsize;
    memset(&pri->pRanges[pri->size - SZINCR], 0, SZINCR * sizeof(xRecordRange));
    if (pri->nRanges < nRanges)
        pri->nRanges = nRanges;
    return Success;
}                               /* RecordAllocRanges */

Project: xorg-xserver
File: record.c

RecordFlushAllContexts(CallbackListPtr *pcbl,
                       void *nulldata, void *calldata)
{
    int eci;                    /* enabled context index */
    RecordContextPtr pContext;

    for (eci = 0; eci < numEnabledContexts; eci++) {
        pContext = ppAllContexts[eci];

        /* In most cases we leave it to RecordFlushReplyBuffer to make
         * this check, but this function could be called very often, so we
         * check before calling hoping to save the function call cost
         * most of the time.
         */
        if (pContext->numBufBytes)
            RecordFlushReplyBuffer(ppAllContexts[eci], NULL, 0, NULL, 0);
    }
}                               /* RecordFlushAllContexts */

Project: xorg-xserver
File: record.c

RecordInstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
{
    int i = 0;
    XID client;

    if (oneclient)
        client = oneclient;
    else
        client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;

    while (client) {
        if (client != XRecordFutureClients) {
            if (pRCAP->pRequestMajorOpSet) {
                RecordSetIteratePtr pIter = NULL;
                RecordSetInterval interval;
                ClientPtr pClient = clients[CLIENT_ID(client)];

                if (pClient && !RecordClientPrivate(pClient)) {
                    RecordClientPrivatePtr pClientPriv;

                    /* no Record proc vector; allocate one */
                    pClientPriv = (RecordClientPrivatePtr)
                        malloc(sizeof(RecordClientPrivateRec));
                    if (!pClientPriv)
                        return BadAlloc;
                    /* copy old proc vector to new */
                    memcpy(pClientPriv->recordVector, pClient->requestVector,
                           sizeof(pClientPriv->recordVector));
                    pClientPriv->originalVector = pClient->requestVector;
                    dixSetPrivate(&pClient->devPrivates,
                                  RecordClientPrivateKey, pClientPriv);
                    pClient->requestVector = pClientPriv->recordVector;
                }
                while ((pIter = RecordIterateSet(pRCAP->pRequestMajorOpSet,
                                                 pIter, &interval))) {
                    unsigned int j;

                    for (j = interval.first; j <= interval.last; j++)
                        pClient->requestVector[j] = RecordARequest;
                }
            }
        }
        if (oneclient)
            client = 0;
        else
            client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
    }

    assert(numEnabledRCAPs >= 0);
    if (!oneclient && ++numEnabledRCAPs == 1) { /* we're enabling the first context */
        if (!AddCallback(&EventCallback, RecordADeliveredEventOrError, NULL))
            return BadAlloc;
        if (!AddCallback(&DeviceEventCallback, RecordADeviceEvent, NULL))
            return BadAlloc;
        if (!AddCallback(&ReplyCallback, RecordAReply, NULL))
            return BadAlloc;
        if (!AddCallback(&FlushCallback, RecordFlushAllContexts, NULL))
            return BadAlloc;
        /* Alternate context flushing scheme: delete the line above
         * and call RegisterBlockAndWakeupHandlers here passing
         * RecordFlushAllContexts.  Is this any better?
         */
    }
    return Success;
}                               /* RecordInstallHooks */

Project: xorg-xserver
File: record.c

RecordSanityCheckRegisterClients(RecordContextPtr pContext, ClientPtr client,
                                 xRecordRegisterClientsReq * stuff)
{
    int err;
    xRecordRange *pRange;
    int i;
    XID recordingClient;

    if (((client->req_len << 2) - SIZEOF(xRecordRegisterClientsReq)) !=
        4 * stuff->nClients + SIZEOF(xRecordRange) * stuff->nRanges)
        return BadLength;

    if (stuff->elementHeader &
        ~(XRecordFromClientSequence | XRecordFromClientTime |
          XRecordFromServerTime)) {
        client->errorValue = stuff->elementHeader;
        return BadValue;
    }

    recordingClient = pContext->pRecordingClient ?
        pContext->pRecordingClient->clientAsMask : 0;
    err = RecordSanityCheckClientSpecifiers(client, (XID *) &stuff[1],
                                            stuff->nClients, recordingClient);
    if (err != Success)
        return err;

    pRange = (xRecordRange *) (((XID *) &stuff[1]) + stuff->nClients);
    for (i = 0; i < stuff->nRanges; i++, pRange++) {
        if (pRange->coreRequestsFirst > pRange->coreRequestsLast) {
            client->errorValue = pRange->coreRequestsFirst;
            return BadValue;
        }
        if (pRange->coreRepliesFirst > pRange->coreRepliesLast) {
            client->errorValue = pRange->coreRepliesFirst;
            return BadValue;
        }
        if ((pRange->extRequestsMajorFirst || pRange->extRequestsMajorLast) &&
            (pRange->extRequestsMajorFirst < 128 ||
             pRange->extRequestsMajorLast < 128 ||
             pRange->extRequestsMajorFirst > pRange->extRequestsMajorLast)) {
            client->errorValue = pRange->extRequestsMajorFirst;
            return BadValue;
        }
        if (pRange->extRequestsMinorFirst > pRange->extRequestsMinorLast) {
            client->errorValue = pRange->extRequestsMinorFirst;
            return BadValue;
        }
        if ((pRange->extRepliesMajorFirst || pRange->extRepliesMajorLast) &&
            (pRange->extRepliesMajorFirst < 128 ||
             pRange->extRepliesMajorLast < 128 ||
             pRange->extRepliesMajorFirst > pRange->extRepliesMajorLast)) {
            client->errorValue = pRange->extRepliesMajorFirst;
            return BadValue;
        }
        if (pRange->extRepliesMinorFirst > pRange->extRepliesMinorLast) {
            client->errorValue = pRange->extRepliesMinorFirst;
            return BadValue;
        }
        if ((pRange->deliveredEventsFirst || pRange->deliveredEventsLast) &&
            (pRange->deliveredEventsFirst < 2 ||
             pRange->deliveredEventsLast < 2 ||
             pRange->deliveredEventsFirst > pRange->deliveredEventsLast)) {
            client->errorValue = pRange->deliveredEventsFirst;
            return BadValue;
        }
        if ((pRange->deviceEventsFirst || pRange->deviceEventsLast) &&
            (pRange->deviceEventsFirst < 2 ||
             pRange->deviceEventsLast < 2 ||
             pRange->deviceEventsFirst > pRange->deviceEventsLast)) {
            client->errorValue = pRange->deviceEventsFirst;
            return BadValue;
        }
        if (pRange->errorsFirst > pRange->errorsLast) {
            client->errorValue = pRange->errorsFirst;
            return BadValue;
        }
        if (pRange->clientStarted != xFalse && pRange->clientStarted != xTrue) {
            client->errorValue = pRange->clientStarted;
            return BadValue;
        }
        if (pRange->clientDied != xFalse && pRange->clientDied != xTrue) {
            client->errorValue = pRange->clientDied;
            return BadValue;
        }
    }                           /* end for each range */
    return Success;
}                               /* end RecordSanityCheckRegisterClients */

Project: xorg-xserver
File: record.c

ProcRecordGetContext(ClientPtr client)
{
    RecordContextPtr pContext;

    REQUEST(xRecordGetContextReq);
    xRecordGetContextReply rep;
    RecordClientsAndProtocolPtr pRCAP;
    int nRCAPs = 0;
    GetContextRangeInfoPtr pRangeInfo;
    GetContextRangeInfoPtr pri;
    int i;
    int err;
    CARD32 nClients, length;

    REQUEST_SIZE_MATCH(xRecordGetContextReq);
    VERIFY_CONTEXT(pContext, stuff->context, client);

    /* how many RCAPs are there on this context? */

    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
        nRCAPs++;

    /* allocate and initialize space for record range info */

    pRangeInfo = xallocarray(nRCAPs, sizeof(GetContextRangeInfoRec));
    if (!pRangeInfo && nRCAPs > 0)
        return BadAlloc;
    for (i = 0; i < nRCAPs; i++) {
        pRangeInfo[i].pRanges = NULL;
        pRangeInfo[i].size = 0;
        pRangeInfo[i].nRanges = 0;
    }

    /* convert the RCAP (internal) representation of the recorded protocol
     * to the wire protocol (external) representation, storing the information
     * for the ith RCAP in pri[i]
     */

    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
         pRCAP; pRCAP = pRCAP->pNextRCAP, pri++) {
        xRecordRange rr;

        err = RecordConvertSetToRanges(pRCAP->pRequestMajorOpSet, pri,
                                       offset_of(rr, coreRequestsFirst), TRUE,
                                       127, NULL);
        if (err != Success)
            goto bailout;

        err = RecordConvertSetToRanges(pRCAP->pReplyMajorOpSet, pri,
                                       offset_of(rr, coreRepliesFirst), TRUE,
                                       127, NULL);
        if (err != Success)
            goto bailout;

        err = RecordConvertSetToRanges(pRCAP->pDeliveredEventSet, pri,
                                       offset_of(rr, deliveredEventsFirst),
                                       TRUE, 255, NULL);
        if (err != Success)
            goto bailout;

        err = RecordConvertSetToRanges(pRCAP->pDeviceEventSet, pri,
                                       offset_of(rr, deviceEventsFirst), TRUE,
                                       255, NULL);
        if (err != Success)
            goto bailout;

        err = RecordConvertSetToRanges(pRCAP->pErrorSet, pri,
                                       offset_of(rr, errorsFirst), TRUE, 255,
                                       NULL);
        if (err != Success)
            goto bailout;

        err = RecordConvertMinorOpInfoToRanges(pRCAP->pRequestMinOpInfo,
                                               pri, offset_of(rr,
                                                              extRequestsMajorFirst));
        if (err != Success)
            goto bailout;

        err = RecordConvertMinorOpInfoToRanges(pRCAP->pReplyMinOpInfo,
                                               pri, offset_of(rr,
                                                              extRepliesMajorFirst));
        if (err != Success)
            goto bailout;

        if (pRCAP->clientStarted || pRCAP->clientDied) {
            if (pri->nRanges == 0)
                RecordAllocRanges(pri, 1);
            pri->pRanges[0].clientStarted = pRCAP->clientStarted;
            pri->pRanges[0].clientDied = pRCAP->clientDied;
        }
    }

    /* calculate number of clients and reply length */

    nClients = 0;
    length = 0;
    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
         pRCAP; pRCAP = pRCAP->pNextRCAP, pri++) {
        nClients += pRCAP->numClients;
        length += pRCAP->numClients *
            (bytes_to_int32(sizeof(xRecordClientInfo)) +
             pri->nRanges * bytes_to_int32(sizeof(xRecordRange)));
    }

    /* write the reply header */

    rep = (xRecordGetContextReply) {
        .type = X_Reply,
        .enabled = pContext->pRecordingClient != NULL,
        .sequenceNumber = client->sequence,
        .length = length,
        .elementHeader = pContext->elemHeaders,
        .nClients = nClients
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.nClients);
    }
    WriteToClient(client, sizeof(xRecordGetContextReply), &rep);

    /* write all the CLIENT_INFOs */

    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
         pRCAP; pRCAP = pRCAP->pNextRCAP, pri++) {
        xRecordClientInfo rci;

        rci.nRanges = pri->nRanges;
        if (client->swapped) {
            swapl(&rci.nRanges);
            RecordSwapRanges(pri->pRanges, pri->nRanges);
        }
        for (i = 0; i < pRCAP->numClients; i++) {
            rci.clientResource = pRCAP->pClientIDs[i];
            if (client->swapped)
                swapl(&rci.clientResource);
            WriteToClient(client, sizeof(xRecordClientInfo), &rci);
            WriteToClient(client, sizeof(xRecordRange) * pri->nRanges,
                          pri->pRanges);
        }
    }
    err = Success;

 bailout:
    for (i = 0; i < nRCAPs; i++) {
        free(pRangeInfo[i].pRanges);
    }
    free(pRangeInfo);
    return err;
}                               /* ProcRecordGetContext */

Project: xorg-xserver
File: record.c

RecordDeleteContext(void *value, XID id)
{
    int i;
    RecordContextPtr pContext = (RecordContextPtr) value;
    RecordClientsAndProtocolPtr pRCAP;

    RecordDisableContext(pContext);

    /*  Remove all the clients from all the RCAPs.
     *  As a result, the RCAPs will be freed.
     */

    while ((pRCAP = pContext->pListOfRCAP)) {
        int numClients = pRCAP->numClients;

        /* when the last client is deleted, the RCAP will go away. */
        while (numClients--) {
            RecordDeleteClientFromRCAP(pRCAP, numClients);
        }
    }

    /* remove context from AllContexts list */

    if (-1 != (i = RecordFindContextOnAllContexts(pContext))) {
        ppAllContexts[i] = ppAllContexts[numContexts - 1];
        if (--numContexts == 0) {
            free(ppAllContexts);
            ppAllContexts = NULL;
        }
    }
    free(pContext);

    return Success;
}                               /* RecordDeleteContext */

Project: xorg-xserver
File: record.c

SProcRecordGetContext(ClientPtr client)
{
    REQUEST(xRecordGetContextReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xRecordGetContextReq);
    swapl(&stuff->context);
    return ProcRecordGetContext(client);
}                               /* SProcRecordGetContext */

Project: xorg-xserver
File: record.c

RecordConvertRangesToIntervals(SetInfoPtr psi,
                               xRecordRange * pRanges,
                               int nRanges,
                               int byteoffset,
                               SetInfoPtr pExtSetInfo, int *pnExtSetInfo)
{
    int i;
    CARD8 *pCARD8;
    int first, last;
    int err;

    for (i = 0; i < nRanges; i++, pRanges++) {
        pCARD8 = ((CARD8 *) pRanges) + byteoffset;
        first = pCARD8[0];
        last = pCARD8[1];
        if (first || last) {
            if (!psi->intervals) {
                err = RecordAllocIntervals(psi, 2 * (nRanges - i));
                if (err != Success)
                    return err;
            }
            psi->intervals[psi->nintervals].first = first;
            psi->intervals[psi->nintervals].last = last;
            psi->nintervals++;
            assert(psi->nintervals <= psi->size);
            if (pExtSetInfo) {
                SetInfoPtr pesi = pExtSetInfo;
                CARD16 *pCARD16 = (CARD16 *) (pCARD8 + 2);
                int j;

                for (j = 0; j < *pnExtSetInfo; j++, pesi++) {
                    if ((first == pesi->first) && (last == pesi->last))
                        break;
                }
                if (j == *pnExtSetInfo) {
                    err = RecordAllocIntervals(pesi, 2 * (nRanges - i));
                    if (err != Success)
                        return err;
                    pesi->first = first;
                    pesi->last = last;
                    (*pnExtSetInfo)++;
                }
                pesi->intervals[pesi->nintervals].first = pCARD16[0];
                pesi->intervals[pesi->nintervals].last = pCARD16[1];
                pesi->nintervals++;
                assert(pesi->nintervals <= pesi->size);
            }
        }
    }
    return Success;
}                               /* end RecordConvertRangesToIntervals */

Project: xorg-xserver
File: record.c

SProcRecordUnregisterClients(ClientPtr client)
{
    REQUEST(xRecordUnregisterClientsReq);

    swaps(&stuff->length);
    REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
    swapl(&stuff->context);
    swapl(&stuff->nClients);
    SwapRestL(stuff);
    return ProcRecordUnregisterClients(client);
}                               /* SProcRecordUnregisterClients */

Project: xorg-xserver
File: record.c

SProcRecordEnableContext(ClientPtr client)
{
    REQUEST(xRecordEnableContextReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xRecordEnableContextReq);
    swapl(&stuff->context);
    return ProcRecordEnableContext(client);
}                               /* SProcRecordEnableContext */

Project: xorg-xserver
File: record.c

RecordAddClientToRCAP(RecordClientsAndProtocolPtr pRCAP, XID clientspec)
{
    if (pRCAP->numClients == pRCAP->sizeClients) {
        if (pRCAP->clientIDsSeparatelyAllocated) {
            XID *pNewIDs =
                reallocarray(pRCAP->pClientIDs,
                             pRCAP->sizeClients + CLIENT_ARRAY_GROWTH_INCREMENT,
                             sizeof(XID));
            if (!pNewIDs)
                return;
            pRCAP->pClientIDs = pNewIDs;
            pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
        }
        else {
            XID *pNewIDs =
                xallocarray(pRCAP->sizeClients + CLIENT_ARRAY_GROWTH_INCREMENT,
                            sizeof(XID));
            if (!pNewIDs)
                return;
            memcpy(pNewIDs, pRCAP->pClientIDs, pRCAP->numClients * sizeof(XID));
            pRCAP->pClientIDs = pNewIDs;
            pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
            pRCAP->clientIDsSeparatelyAllocated = 1;
        }
    }
    pRCAP->pClientIDs[pRCAP->numClients++] = clientspec;
    if (pRCAP->pContext->pRecordingClient)
        RecordInstallHooks(pRCAP, clientspec);
}                               /* RecordDeleteClientFromRCAP */

Project: xorg-xserver
File: record.c

RecordSendProtocolEvents(RecordClientsAndProtocolPtr pRCAP,
                         RecordContextPtr pContext, xEvent *pev, int count)
{
    int ev;                     /* event index */

    for (ev = 0; ev < count; ev++, pev++) {
        if (RecordIsMemberOfSet(pRCAP->pDeviceEventSet, pev->u.u.type & 0177)) {
            xEvent swappedEvent;
            xEvent *pEvToRecord = pev;

#ifdef PANORAMIX
            xEvent shiftedEvent;

            if (!noPanoramiXExtension &&
                (pev->u.u.type == MotionNotify ||
                 pev->u.u.type == ButtonPress ||
                 pev->u.u.type == ButtonRelease ||
                 pev->u.u.type == KeyPress || pev->u.u.type == KeyRelease)) {
                int scr = XineramaGetCursorScreen(inputInfo.pointer);

                memcpy(&shiftedEvent, pev, sizeof(xEvent));
                shiftedEvent.u.keyButtonPointer.rootX +=
                    screenInfo.screens[scr]->x - screenInfo.screens[0]->x;
                shiftedEvent.u.keyButtonPointer.rootY +=
                    screenInfo.screens[scr]->y - screenInfo.screens[0]->y;
                pEvToRecord = &shiftedEvent;
            }
#endif                          /* PANORAMIX */

            if (pContext->pRecordingClient->swapped) {
                (*EventSwapVector[pEvToRecord->u.u.type & 0177])
                    (pEvToRecord, &swappedEvent);
                pEvToRecord = &swappedEvent;
            }

            RecordAProtocolElement(pContext, NULL,
                                   XRecordFromServer, pEvToRecord,
                                   SIZEOF(xEvent), 0, 0);
            /* make sure device events get flushed in the absence
             * of other client activity
             */
            SetCriticalOutputPending();
        }
    }                           /* end for each event */

}                               /* RecordADeviceEvent */

Project: xorg-xserver
File: record.c

SProcRecordQueryVersion(ClientPtr client)
{
    REQUEST(xRecordQueryVersionReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
    swaps(&stuff->majorVersion);
    swaps(&stuff->minorVersion);
    return ProcRecordQueryVersion(client);
}                               /* SProcRecordQueryVersion */

Project: xorg-xserver
File: record.c

RecordFindContextOnAllContexts(RecordContextPtr pContext)
{
    int i;

    assert(numContexts >= numEnabledContexts);
    for (i = 0; i < numContexts; i++) {
        if (ppAllContexts[i] == pContext)
            return i;
    }
    return -1;
}                               /* RecordFindContextOnAllContexts */

Project: xorg-xserver
File: record.c

RecordAProtocolElement(RecordContextPtr pContext, ClientPtr pClient,
                       int category, void *data, int datalen, int padlen,
                       int futurelen)
{
    CARD32 elemHeaderData[2];
    int numElemHeaders = 0;
    Bool recordingClientSwapped = pContext->pRecordingClient->swapped;
    CARD32 serverTime = 0;
    Bool gotServerTime = FALSE;
    int replylen;

    if (futurelen >= 0) {       /* start of new protocol element */
        xRecordEnableContextReply *pRep = (xRecordEnableContextReply *)
            pContext->replyBuffer;

        if (pContext->pBufClient != pClient ||
            pContext->bufCategory != category) {
            RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
            pContext->pBufClient = pClient;
            pContext->bufCategory = category;
        }

        if (!pContext->numBufBytes) {
            serverTime = GetTimeInMillis();
            gotServerTime = TRUE;
            pRep->type = X_Reply;
            pRep->category = category;
            pRep->sequenceNumber = pContext->pRecordingClient->sequence;
            pRep->length = 0;
            pRep->elementHeader = pContext->elemHeaders;
            pRep->serverTime = serverTime;
            if (pClient) {
                pRep->clientSwapped =
                    (pClient->swapped != recordingClientSwapped);
                pRep->idBase = pClient->clientAsMask;
                pRep->recordedSequenceNumber = pClient->sequence;
            }
            else {              /* it's a device event, StartOfData, or EndOfData */

                pRep->clientSwapped = (category != XRecordFromServer) &&
                    recordingClientSwapped;
                pRep->idBase = 0;
                pRep->recordedSequenceNumber = 0;
            }

            if (recordingClientSwapped) {
                swaps(&pRep->sequenceNumber);
                swapl(&pRep->length);
                swapl(&pRep->idBase);
                swapl(&pRep->serverTime);
                swapl(&pRep->recordedSequenceNumber);
            }
            pContext->numBufBytes = SIZEOF(xRecordEnableContextReply);
        }

        /* generate element headers if needed */

        if (((pContext->elemHeaders & XRecordFromClientTime)
             && category == XRecordFromClient)
            || ((pContext->elemHeaders & XRecordFromServerTime)
                && category == XRecordFromServer)) {
            if (gotServerTime)
                elemHeaderData[numElemHeaders] = serverTime;
            else
                elemHeaderData[numElemHeaders] = GetTimeInMillis();
            if (recordingClientSwapped)
                swapl(&elemHeaderData[numElemHeaders]);
            numElemHeaders++;
        }

        if ((pContext->elemHeaders & XRecordFromClientSequence)
            && (category == XRecordFromClient || category == XRecordClientDied)) {
            elemHeaderData[numElemHeaders] = pClient->sequence;
            if (recordingClientSwapped)
                swapl(&elemHeaderData[numElemHeaders]);
            numElemHeaders++;
        }

        /* adjust reply length */

        replylen = pRep->length;
        if (recordingClientSwapped)
            swapl(&replylen);
        replylen += numElemHeaders + bytes_to_int32(datalen) +
            bytes_to_int32(futurelen);
        if (recordingClientSwapped)
            swapl(&replylen);
        pRep->length = replylen;
    }                           /* end if not continued reply */

    numElemHeaders *= 4;

    /* if space available >= space needed, buffer the data */

    if (REPLY_BUF_SIZE - pContext->numBufBytes >= datalen + numElemHeaders) {
        if (numElemHeaders) {
            memcpy(pContext->replyBuffer + pContext->numBufBytes,
                   elemHeaderData, numElemHeaders);
            pContext->numBufBytes += numElemHeaders;
        }
        if (datalen) {
            static char padBuffer[3];   /* as in FlushClient */

            memcpy(pContext->replyBuffer + pContext->numBufBytes,
                   data, datalen - padlen);
            pContext->numBufBytes += datalen - padlen;
            memcpy(pContext->replyBuffer + pContext->numBufBytes,
                   padBuffer, padlen);
            pContext->numBufBytes += padlen;
        }
    }
    else {
        RecordFlushReplyBuffer(pContext, (void *) elemHeaderData,
                               numElemHeaders, (void *) data,
                               datalen - padlen);
    }
}                               /* RecordAProtocolElement */

Project: xorg-xserver
File: record.c

RecordAReply(CallbackListPtr *pcbl, void *nulldata, void *calldata)
{
    RecordContextPtr pContext;
    RecordClientsAndProtocolPtr pRCAP;
    int eci;
    ReplyInfoRec *pri = (ReplyInfoRec *) calldata;
    ClientPtr client = pri->client;

    for (eci = 0; eci < numEnabledContexts; eci++) {
        pContext = ppAllContexts[eci];
        pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask, NULL);
        if (pRCAP) {
            int majorop = client->majorOp;

            if (pContext->continuedReply) {
                RecordAProtocolElement(pContext, client, XRecordFromServer,
                                       (void *) pri->replyData,
                                       pri->dataLenBytes, pri->padBytes,
                                       /* continuation */ -1);
                if (!pri->bytesRemaining)
                    pContext->continuedReply = 0;
            }
            else if (pri->startOfReply && pRCAP->pReplyMajorOpSet &&
                     RecordIsMemberOfSet(pRCAP->pReplyMajorOpSet, majorop)) {
                if (majorop <= 127) {   /* core reply */
                    RecordAProtocolElement(pContext, client, XRecordFromServer,
                                           (void *) pri->replyData,
                                           pri->dataLenBytes, 0,
                                           pri->bytesRemaining);
                    if (pri->bytesRemaining)
                        pContext->continuedReply = 1;
                }
                else {          /* extension, check minor opcode */

                    int minorop = client->minorOp;
                    int numMinOpInfo;
                    RecordMinorOpPtr pMinorOpInfo = pRCAP->pReplyMinOpInfo;

                    assert(pMinorOpInfo);
                    numMinOpInfo = pMinorOpInfo->count;
                    pMinorOpInfo++;
                    assert(numMinOpInfo);
                    for (; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++) {
                        if (majorop >= pMinorOpInfo->major.first &&
                            majorop <= pMinorOpInfo->major.last &&
                            RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
                                                minorop)) {
                            RecordAProtocolElement(pContext, client,
                                                   XRecordFromServer,
                                                   (void *) pri->replyData,
                                                   pri->dataLenBytes, 0,
                                                   pri->bytesRemaining);
                            if (pri->bytesRemaining)
                                pContext->continuedReply = 1;
                            break;
                        }
                    }           /* end for each minor op info */
                }               /* end extension reply */
            }                   /* end continued reply vs. start of reply */
        }                       /* end client is registered on this context */
    }                           /* end for each context */
}                               /* RecordAReply */

Project: xorg-xserver
File: record.c

RecordUninstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
{
    int i = 0;
    XID client;

    if (oneclient)
        client = oneclient;
    else
        client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;

    while (client) {
        if (client != XRecordFutureClients) {
            if (pRCAP->pRequestMajorOpSet) {
                ClientPtr pClient = clients[CLIENT_ID(client)];
                int c;
                Bool otherRCAPwantsProcVector = FALSE;
                RecordClientPrivatePtr pClientPriv = NULL;

                assert(pClient);
                pClientPriv = RecordClientPrivate(pClient);
                assert(pClientPriv);
                memcpy(pClientPriv->recordVector, pClientPriv->originalVector,
                       sizeof(pClientPriv->recordVector));

                for (c = 0; c < numEnabledContexts; c++) {
                    RecordClientsAndProtocolPtr pOtherRCAP;
                    RecordContextPtr pContext = ppAllContexts[c];

                    if (pContext == pRCAP->pContext)
                        continue;
                    pOtherRCAP = RecordFindClientOnContext(pContext, client,
                                                           NULL);
                    if (pOtherRCAP && pOtherRCAP->pRequestMajorOpSet) {
                        RecordSetIteratePtr pIter = NULL;
                        RecordSetInterval interval;

                        otherRCAPwantsProcVector = TRUE;
                        while ((pIter =
                                RecordIterateSet(pOtherRCAP->pRequestMajorOpSet,
                                                 pIter, &interval))) {
                            unsigned int j;

                            for (j = interval.first; j <= interval.last; j++)
                                pClient->requestVector[j] = RecordARequest;
                        }
                    }
                }
                if (!otherRCAPwantsProcVector) {        /* nobody needs it, so free it */
                    pClient->requestVector = pClientPriv->originalVector;
                    dixSetPrivate(&pClient->devPrivates,
                                  RecordClientPrivateKey, NULL);
                    free(pClientPriv);
                }
            }                   /* end if this RCAP specifies any requests */
        }                       /* end if not future clients */
        if (oneclient)
            client = 0;
        else
            client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
    }

    assert(numEnabledRCAPs >= 1);
    if (!oneclient && --numEnabledRCAPs == 0) { /* we're disabling the last context */
        DeleteCallback(&EventCallback, RecordADeliveredEventOrError, NULL);
        DeleteCallback(&DeviceEventCallback, RecordADeviceEvent, NULL);
        DeleteCallback(&ReplyCallback, RecordAReply, NULL);
        DeleteCallback(&FlushCallback, RecordFlushAllContexts, NULL);
        /* Alternate context flushing scheme: delete the line above
         * and call RemoveBlockAndWakeupHandlers here passing
         * RecordFlushAllContexts.  Is this any better?
         */
        /* Having deleted the callback, call it one last time. -gildea */
        RecordFlushAllContexts(&FlushCallback, NULL, NULL);
    }
}                               /* RecordUninstallHooks */

Project: xorg-xserver
File: record.c

SProcRecordCreateContext(ClientPtr client)
{
    REQUEST(xRecordCreateContextReq);
    int status;

    swaps(&stuff->length);
    REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
    if ((status = SwapCreateRegister(client, (void *) stuff)) != Success)
        return status;
    return ProcRecordCreateContext(client);
}                               /* SProcRecordCreateContext */

Project: xorg-xserver
File: record.c

RecordSwapRanges(xRecordRange * pRanges, int nRanges)
{
    int i;

    for (i = 0; i < nRanges; i++, pRanges++) {
        swaps(&pRanges->extRequestsMinorFirst);
        swaps(&pRanges->extRequestsMinorLast);
        swaps(&pRanges->extRepliesMinorFirst);
        swaps(&pRanges->extRepliesMinorLast);
    }
}                               /* RecordSwapRanges */

Project: xorg-xserver
File: record.c

RecordADeviceEvent(CallbackListPtr *pcbl, void *nulldata, void *calldata)
{
    DeviceEventInfoRec *pei = (DeviceEventInfoRec *) calldata;
    RecordContextPtr pContext;
    RecordClientsAndProtocolPtr pRCAP;
    int eci;                    /* enabled context index */

    for (eci = 0; eci < numEnabledContexts; eci++) {
        pContext = ppAllContexts[eci];
        for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
            if (pRCAP->pDeviceEventSet) {
                int count;
                xEvent *xi_events = NULL;

                /* TODO check return values */
                if (IsMaster(pei->device)) {
                    xEvent *core_events;

                    EventToCore(pei->event, &core_events, &count);
                    RecordSendProtocolEvents(pRCAP, pContext, core_events,
                                             count);
                    free(core_events);
                }

                EventToXI(pei->event, &xi_events, &count);
                RecordSendProtocolEvents(pRCAP, pContext, xi_events, count);
                free(xi_events);
            }                   /* end this RCAP selects device events */
        }                       /* end for each RCAP on this context */
    }                           /* end for each enabled context */
}

Project: xorg-xserver
File: record.c

RecordARequest(ClientPtr client)
{
    RecordContextPtr pContext;
    RecordClientsAndProtocolPtr pRCAP;
    int i;
    RecordClientPrivatePtr pClientPriv;

    REQUEST(xReq);
    int majorop;

    majorop = stuff->reqType;
    for (i = 0; i < numEnabledContexts; i++) {
        pContext = ppAllContexts[i];
        pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask, NULL);
        if (pRCAP && pRCAP->pRequestMajorOpSet &&
            RecordIsMemberOfSet(pRCAP->pRequestMajorOpSet, majorop)) {
            if (majorop <= 127) {       /* core request */

                if (stuff->length == 0)
                    RecordABigRequest(pContext, client, stuff);
                else
                    RecordAProtocolElement(pContext, client, XRecordFromClient,
                                           (void *) stuff,
                                           client->req_len << 2, 0, 0);
            }
            else {              /* extension, check minor opcode */

                int minorop = client->minorOp;
                int numMinOpInfo;
                RecordMinorOpPtr pMinorOpInfo = pRCAP->pRequestMinOpInfo;

                assert(pMinorOpInfo);
                numMinOpInfo = pMinorOpInfo->count;
                pMinorOpInfo++;
                assert(numMinOpInfo);
                for (; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++) {
                    if (majorop >= pMinorOpInfo->major.first &&
                        majorop <= pMinorOpInfo->major.last &&
                        RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
                                            minorop)) {
                        if (stuff->length == 0)
                            RecordABigRequest(pContext, client, stuff);
                        else
                            RecordAProtocolElement(pContext, client,
                                                   XRecordFromClient,
                                                   (void *) stuff,
                                                   client->req_len << 2, 0, 0);
                        break;
                    }
                }               /* end for each minor op info */
            }                   /* end extension request */
        }                       /* end this RCAP wants this major opcode */
    }                           /* end for each context */
    pClientPriv = RecordClientPrivate(client);
    assert(pClientPriv);
    return (*pClientPriv->originalVector[majorop]) (client);
}                               /* RecordARequest */

Project: xorg-xserver
File: record.c

ProcRecordFreeContext(ClientPtr client)
{
    RecordContextPtr pContext;

    REQUEST(xRecordFreeContextReq);

    REQUEST_SIZE_MATCH(xRecordFreeContextReq);
    VERIFY_CONTEXT(pContext, stuff->context, client);
    FreeResource(stuff->context, RT_NONE);
    return Success;
}                               /* ProcRecordFreeContext */

Project: xorg-xserver
File: record.c

RecordFlushReplyBuffer(RecordContextPtr pContext,
                       void *data1, int len1, void *data2, int len2)
{
    if (!pContext->pRecordingClient || pContext->pRecordingClient->clientGone ||
        pContext->inFlush)
        return;
    ++pContext->inFlush;
    if (pContext->numBufBytes)
        WriteToClient(pContext->pRecordingClient, pContext->numBufBytes,
                      pContext->replyBuffer);
    pContext->numBufBytes = 0;
    if (len1)
        WriteToClient(pContext->pRecordingClient, len1, data1);
    if (len2)
        WriteToClient(pContext->pRecordingClient, len2, data2);
    --pContext->inFlush;
}                               /* RecordFlushReplyBuffer */

Project: xorg-xserver
File: record.c

RecordAClientStateChange(CallbackListPtr *pcbl, void *nulldata,
                         void *calldata)
{
    NewClientInfoRec *pci = (NewClientInfoRec *) calldata;
    int i;
    ClientPtr pClient = pci->client;
    RecordContextPtr *ppAllContextsCopy = NULL;
    int numContextsCopy = 0;

    switch (pClient->clientState) {
    case ClientStateRunning:   /* new client */
        for (i = 0; i < numContexts; i++) {
            RecordClientsAndProtocolPtr pRCAP;
            RecordContextPtr pContext = ppAllContexts[i];

            if ((pRCAP = RecordFindClientOnContext(pContext,
                                                   XRecordFutureClients, NULL)))
            {
                RecordAddClientToRCAP(pRCAP, pClient->clientAsMask);
                if (pContext->pRecordingClient && pRCAP->clientStarted)
                    RecordConnectionSetupInfo(pContext, pci);
            }
        }
        break;

    case ClientStateGone:
    case ClientStateRetained:  /* client disconnected */

        /* RecordDisableContext modifies contents of ppAllContexts. */
        if (!(numContextsCopy = numContexts))
            break;
        ppAllContextsCopy = xallocarray(numContextsCopy,
                                        sizeof(RecordContextPtr));
        assert(ppAllContextsCopy);
        memcpy(ppAllContextsCopy, ppAllContexts,
               numContextsCopy * sizeof(RecordContextPtr));

        for (i = 0; i < numContextsCopy; i++) {
            RecordClientsAndProtocolPtr pRCAP;
            RecordContextPtr pContext = ppAllContextsCopy[i];
            int pos;

            if (pContext->pRecordingClient == pClient)
                RecordDisableContext(pContext);
            if ((pRCAP = RecordFindClientOnContext(pContext,
                                                   pClient->clientAsMask,
                                                   &pos))) {
                if (pContext->pRecordingClient && pRCAP->clientDied)
                    RecordAProtocolElement(pContext, pClient,
                                           XRecordClientDied, NULL, 0, 0, 0);
                RecordDeleteClientFromRCAP(pRCAP, pos);
            }
        }

        free(ppAllContextsCopy);
        break;

    default:
        break;
    }                           /* end switch on client state */
}                               /* RecordAClientStateChange */

Project: xorg-xserver
File: record.c

RecordPadAlign(int size, int align)
{
    return (align - (size & (align - 1))) & (align - 1);
}                               /* RecordPadAlign */

Project: xorg-xserver
File: record.c

RecordConvertMinorOpInfoToRanges(RecordMinorOpPtr pMinOpInfo,
                                 GetContextRangeInfoPtr pri, int byteoffset)
{
    int nsets;
    int start;
    int i;
    int err;

    if (!pMinOpInfo)
        return Success;

    nsets = pMinOpInfo->count;
    pMinOpInfo++;
    start = 0;
    for (i = 0; i < nsets; i++) {
        int j, s;

        s = start;
        err = RecordConvertSetToRanges(pMinOpInfo[i].major.pMinOpSet, pri,
                                       byteoffset + 2, FALSE, 65535, &start);
        if (err != Success)
            return err;
        for (j = s; j < start; j++) {
            CARD8 *pCARD8 = ((CARD8 *) &pri->pRanges[j]) + byteoffset;

            *pCARD8++ = pMinOpInfo[i].major.first;
            *pCARD8 = pMinOpInfo[i].major.last;
        }
    }
    return Success;
}                               /* RecordConvertMinorOpInfoToRanges */

Project: xorg-xserver
File: record.c

RecordAllocIntervals(SetInfoPtr psi, int nIntervals)
{
    assert(!psi->intervals);
    psi->intervals = xallocarray(nIntervals, sizeof(RecordSetInterval));
    if (!psi->intervals)
        return BadAlloc;
    memset(psi->intervals, 0, nIntervals * sizeof(RecordSetInterval));
    psi->size = nIntervals;
    return Success;
}                               /* end RecordAllocIntervals */

Project: xorg-xserver
File: record.c

ProcRecordDisableContext(ClientPtr client)
{
    RecordContextPtr pContext;

    REQUEST(xRecordDisableContextReq);

    REQUEST_SIZE_MATCH(xRecordDisableContextReq);
    VERIFY_CONTEXT(pContext, stuff->context, client);
    RecordDisableContext(pContext);
    return Success;
}                               /* ProcRecordDisableContext */

Project: xorg-xserver
File: record.c

ProcRecordCreateContext(ClientPtr client)
{
    REQUEST(xRecordCreateContextReq);
    RecordContextPtr pContext;
    RecordContextPtr *ppNewAllContexts = NULL;
    int err = BadAlloc;

    REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
    LEGAL_NEW_RESOURCE(stuff->context, client);

    pContext = (RecordContextPtr) malloc(sizeof(RecordContextRec));
    if (!pContext)
        goto bailout;

    /* make sure there is room in ppAllContexts to store the new context */

    ppNewAllContexts =
        reallocarray(ppAllContexts, numContexts + 1, sizeof(RecordContextPtr));
    if (!ppNewAllContexts)
        goto bailout;
    ppAllContexts = ppNewAllContexts;

    pContext->id = stuff->context;
    pContext->pRecordingClient = NULL;
    pContext->pListOfRCAP = NULL;
    pContext->elemHeaders = 0;
    pContext->bufCategory = 0;
    pContext->numBufBytes = 0;
    pContext->pBufClient = NULL;
    pContext->continuedReply = 0;
    pContext->inFlush = 0;

    err = RecordRegisterClients(pContext, client,
                                (xRecordRegisterClientsReq *) stuff);
    if (err != Success)
        goto bailout;

    if (AddResource(pContext->id, RTContext, pContext)) {
        ppAllContexts[numContexts++] = pContext;
        return Success;
    }
    else {
        return BadAlloc;
    }
 bailout:
    free(pContext);
    return err;
}                               /* ProcRecordCreateContext */

Project: xorg-xserver
File: record.c

ProcRecordDispatch(ClientPtr client)
{
    REQUEST(xReq);

    switch (stuff->data) {
    case X_RecordQueryVersion:
        return ProcRecordQueryVersion(client);
    case X_RecordCreateContext:
        return ProcRecordCreateContext(client);
    case X_RecordRegisterClients:
        return ProcRecordRegisterClients(client);
    case X_RecordUnregisterClients:
        return ProcRecordUnregisterClients(client);
    case X_RecordGetContext:
        return ProcRecordGetContext(client);
    case X_RecordEnableContext:
        return ProcRecordEnableContext(client);
    case X_RecordDisableContext:
        return ProcRecordDisableContext(client);
    case X_RecordFreeContext:
        return ProcRecordFreeContext(client);
    default:
        return BadRequest;
    }
}                               /* ProcRecordDispatch */

Project: xorg-xserver
File: record.c

RecordCloseDown(ExtensionEntry * extEntry)
{
    DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
}                               /* RecordCloseDown */

Project: xorg-xserver
File: record.c

RecordConnectionSetupInfo(RecordContextPtr pContext, NewClientInfoRec * pci)
{
    int prefixsize = SIZEOF(xConnSetupPrefix);
    int restsize = pci->prefix->length * 4;

    if (pci->client->swapped) {
        char *pConnSetup = (char *) malloc(prefixsize + restsize);

        if (!pConnSetup)
            return;
        SwapConnSetupPrefix(pci->prefix, (xConnSetupPrefix *) pConnSetup);
        SwapConnSetupInfo((char *) pci->setup,
                          (char *) (pConnSetup + prefixsize));
        RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
                               (void *) pConnSetup, prefixsize + restsize, 0,
                               0);
        free(pConnSetup);
    }
    else {
        /* don't alloc and copy as in the swapped case; just send the
         * data in two pieces
         */
        RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
                               (void *) pci->prefix, prefixsize, 0, restsize);
        RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
                               (void *) pci->setup, restsize, 0,
                               /* continuation */ -1);
    }
}                               /* RecordConnectionSetupInfo */

Project: xorg-xserver
File: record.c

RecordCanonicalizeClientSpecifiers(XID *pClientspecs, int *pNumClientspecs,
                                   XID excludespec)
{
    int i;
    int numClients = *pNumClientspecs;

    /*  first pass strips off the resource index bits, leaving just the
     *  client id bits.  This makes searching for a particular client simpler
     *  (and faster.)
     */
    for (i = 0; i < numClients; i++) {
        XID cs = pClientspecs[i];

        if (cs > XRecordAllClients)
            pClientspecs[i] = CLIENT_BITS(cs);
    }

    for (i = 0; i < numClients; i++) {
        if (pClientspecs[i] == XRecordAllClients || pClientspecs[i] == XRecordCurrentClients) { /* expand All/Current */
            int j, nc;
            XID *pCanon = xallocarray(currentMaxClients + 1, sizeof(XID));

            if (!pCanon)
                return NULL;
            for (nc = 0, j = 1; j < currentMaxClients; j++) {
                ClientPtr client = clients[j];

                if (client != NullClient &&
                    client->clientState == ClientStateRunning &&
                    client->clientAsMask != excludespec) {
                    pCanon[nc++] = client->clientAsMask;
                }
            }
            if (pClientspecs[i] == XRecordAllClients)
                pCanon[nc++] = XRecordFutureClients;
            *pNumClientspecs = nc;
            return pCanon;
        }
        else {                  /* not All or Current */

            int j;

            for (j = i + 1; j < numClients;) {
                if (pClientspecs[i] == pClientspecs[j]) {
                    pClientspecs[j] = pClientspecs[--numClients];
                }
                else
                    j++;
            }
        }
    }                           /* end for each clientspec */
    *pNumClientspecs = numClients;
    return pClientspecs;
}                               /* RecordCanonicalizeClientSpecifiers */

Project: xorg-xserver
File: record.c

ProcRecordQueryVersion(ClientPtr client)
{
    /* REQUEST(xRecordQueryVersionReq); */
    xRecordQueryVersionReply rep = {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .majorVersion = SERVER_RECORD_MAJOR_VERSION,
        .minorVersion = SERVER_RECORD_MINOR_VERSION
    };

    REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swaps(&rep.majorVersion);
        swaps(&rep.minorVersion);
    }
    WriteToClient(client, sizeof(xRecordQueryVersionReply), &rep);
    return Success;
}                               /* ProcRecordQueryVersion */

Project: xorg-xserver
File: record.c

ProcRecordEnableContext(ClientPtr client)
{
    RecordContextPtr pContext;

    REQUEST(xRecordEnableContextReq);
    int i;
    RecordClientsAndProtocolPtr pRCAP;

    REQUEST_SIZE_MATCH(xRecordGetContextReq);
    VERIFY_CONTEXT(pContext, stuff->context, client);
    if (pContext->pRecordingClient)
        return BadMatch;        /* already enabled */

    /* install record hooks for each RCAP */

    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
        int err = RecordInstallHooks(pRCAP, 0);

        if (err != Success) {   /* undo the previous installs */
            RecordClientsAndProtocolPtr pUninstallRCAP;

            for (pUninstallRCAP = pContext->pListOfRCAP;
                 pUninstallRCAP != pRCAP;
                 pUninstallRCAP = pUninstallRCAP->pNextRCAP) {
                RecordUninstallHooks(pUninstallRCAP, 0);
            }
            return err;
        }
    }

    /* Disallow further request processing on this connection until
     * the context is disabled.
     */
    IgnoreClient(client);
    pContext->pRecordingClient = client;

    /* Don't allow the data connection to record itself; unregister it. */
    RecordDeleteClientFromContext(pContext,
                                  pContext->pRecordingClient->clientAsMask);

    /* move the newly enabled context to the front part of ppAllContexts,
     * where all the enabled contexts are
     */
    i = RecordFindContextOnAllContexts(pContext);
    assert(i >= numEnabledContexts);
    if (i != numEnabledContexts) {
        ppAllContexts[i] = ppAllContexts[numEnabledContexts];
        ppAllContexts[numEnabledContexts] = pContext;
    }

    ++numEnabledContexts;
    assert(numEnabledContexts > 0);

    /* send StartOfData */
    RecordAProtocolElement(pContext, NULL, XRecordStartOfData, NULL, 0, 0, 0);
    RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
    return Success;
}                               /* ProcRecordEnableContext */

Project: xorg-xserver
File: record.c

RecordSanityCheckClientSpecifiers(ClientPtr client, XID *clientspecs,
                                  int nspecs, XID errorspec)
{
    int i;
    int clientIndex;
    int rc;
    void *value;

    for (i = 0; i < nspecs; i++) {
        if (clientspecs[i] == XRecordCurrentClients ||
            clientspecs[i] == XRecordFutureClients ||
            clientspecs[i] == XRecordAllClients)
            continue;
        if (errorspec && (CLIENT_BITS(clientspecs[i]) == errorspec))
            return BadMatch;
        clientIndex = CLIENT_ID(clientspecs[i]);
        if (clientIndex && clients[clientIndex] &&
            clients[clientIndex]->clientState == ClientStateRunning) {
            if (clientspecs[i] == clients[clientIndex]->clientAsMask)
                continue;
            rc = dixLookupResourceByClass(&value, clientspecs[i], RC_ANY,
                                          client, DixGetAttrAccess);
            if (rc != Success)
                return rc;
        }
        else
            return BadMatch;
    }
    return Success;
}                               /* RecordSanityCheckClientSpecifiers */

Project: xorg-xserver
File: record.c

RecordDeleteClientFromRCAP(RecordClientsAndProtocolPtr pRCAP, int position)
{
    if (pRCAP->pContext->pRecordingClient)
        RecordUninstallHooks(pRCAP, pRCAP->pClientIDs[position]);
    if (position != pRCAP->numClients - 1)
        pRCAP->pClientIDs[position] = pRCAP->pClientIDs[pRCAP->numClients - 1];
    if (--pRCAP->numClients == 0) {     /* no more clients; remove RCAP from context's list */
        RecordContextPtr pContext = pRCAP->pContext;

        if (pContext->pRecordingClient)
            RecordUninstallHooks(pRCAP, 0);
        if (pContext->pListOfRCAP == pRCAP)
            pContext->pListOfRCAP = pRCAP->pNextRCAP;
        else {
            RecordClientsAndProtocolPtr prevRCAP;

            for (prevRCAP = pContext->pListOfRCAP;
                 prevRCAP->pNextRCAP != pRCAP; prevRCAP = prevRCAP->pNextRCAP);
            prevRCAP->pNextRCAP = pRCAP->pNextRCAP;
        }
        /* free the RCAP */
        if (pRCAP->clientIDsSeparatelyAllocated)
            free(pRCAP->pClientIDs);
        free(pRCAP);
    }
}                               /* RecordDeleteClientFromRCAP */