~ubuntu-branches/ubuntu/dapper/tk8.0/dapper-updates

« back to all changes in this revision

Viewing changes to unix/tkUnixSelect.c

  • Committer: Bazaar Package Importer
  • Author(s): Mike Markley
  • Date: 2001-07-24 21:57:40 UTC
  • Revision ID: james.westby@ubuntu.com-20010724215740-r70t25rtmbqjil2h
Tags: upstream-8.0.5
ImportĀ upstreamĀ versionĀ 8.0.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
 * tkUnixSelect.c --
 
3
 *
 
4
 *      This file contains X specific routines for manipulating 
 
5
 *      selections.
 
6
 *
 
7
 * Copyright (c) 1995 Sun Microsystems, Inc.
 
8
 *
 
9
 * See the file "license.terms" for information on usage and redistribution
 
10
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 
11
 *
 
12
 * RCS: @(#) $Id: tkUnixSelect.c,v 1.2 1998/09/14 18:23:57 stanton Exp $
 
13
 */
 
14
 
 
15
#include "tkInt.h"
 
16
#include "tkSelect.h"
 
17
 
 
18
/*
 
19
 * When handling INCR-style selection retrievals, the selection owner
 
20
 * uses the following data structure to communicate between the
 
21
 * ConvertSelection procedure and TkSelPropProc.
 
22
 */
 
23
 
 
24
typedef struct IncrInfo {
 
25
    TkWindow *winPtr;           /* Window that owns selection. */
 
26
    Atom selection;             /* Selection that is being retrieved. */
 
27
    Atom *multAtoms;            /* Information about conversions to
 
28
                                 * perform:  one or more pairs of
 
29
                                 * (target, property).  This either
 
30
                                 * points to a retrieved  property (for
 
31
                                 * MULTIPLE retrievals) or to a static
 
32
                                 * array. */
 
33
    unsigned long numConversions;
 
34
                                /* Number of entries in offsets (same as
 
35
                                 * # of pairs in multAtoms). */
 
36
    int *offsets;               /* One entry for each pair in
 
37
                                 * multAtoms;  -1 means all data has
 
38
                                 * been transferred for this
 
39
                                 * conversion.  -2 means only the
 
40
                                 * final zero-length transfer still
 
41
                                 * has to be done.  Otherwise it is the
 
42
                                 * offset of the next chunk of data
 
43
                                 * to transfer.  This array is malloc-ed. */
 
44
    int numIncrs;               /* Number of entries in offsets that
 
45
                                 * aren't -1 (i.e. # of INCR-mode transfers
 
46
                                 * not yet completed). */
 
47
    Tcl_TimerToken timeout;     /* Token for timer procedure. */
 
48
    int idleTime;               /* Number of seconds since we heard
 
49
                                 * anything from the selection
 
50
                                 * requestor. */
 
51
    Window reqWindow;           /* Requestor's window id. */
 
52
    Time time;                  /* Timestamp corresponding to
 
53
                                 * selection at beginning of request;
 
54
                                 * used to abort transfer if selection
 
55
                                 * changes. */
 
56
    struct IncrInfo *nextPtr;   /* Next in list of all INCR-style
 
57
                                 * retrievals currently pending. */
 
58
} IncrInfo;
 
59
 
 
60
static IncrInfo *pendingIncrs = NULL;
 
61
                                /* List of all incr structures
 
62
                                 * currently active. */
 
63
 
 
64
/*
 
65
 * Largest property that we'll accept when sending or receiving the
 
66
 * selection:
 
67
 */
 
68
 
 
69
#define MAX_PROP_WORDS 100000
 
70
 
 
71
static TkSelRetrievalInfo *pendingRetrievals = NULL;
 
72
                                /* List of all retrievals currently
 
73
                                 * being waited for. */
 
74
 
 
75
/*
 
76
 * Forward declarations for procedures defined in this file:
 
77
 */
 
78
 
 
79
static void             ConvertSelection _ANSI_ARGS_((TkWindow *winPtr,
 
80
                            XSelectionRequestEvent *eventPtr));
 
81
static void             IncrTimeoutProc _ANSI_ARGS_((ClientData clientData));
 
82
static char *           SelCvtFromX _ANSI_ARGS_((long *propPtr, int numValues,
 
83
                            Atom type, Tk_Window tkwin));
 
84
static long *           SelCvtToX _ANSI_ARGS_((char *string, Atom type,
 
85
                            Tk_Window tkwin, int *numLongsPtr));
 
86
static int              SelectionSize _ANSI_ARGS_((TkSelHandler *selPtr));
 
87
static void             SelRcvIncrProc _ANSI_ARGS_((ClientData clientData,
 
88
                            XEvent *eventPtr));
 
89
static void             SelTimeoutProc _ANSI_ARGS_((ClientData clientData));
 
90
 
 
91
/*
 
92
 *----------------------------------------------------------------------
 
93
 *
 
94
 * TkSelGetSelection --
 
95
 *
 
96
 *      Retrieve the specified selection from another process.
 
97
 *
 
98
 * Results:
 
99
 *      The return value is a standard Tcl return value.
 
100
 *      If an error occurs (such as no selection exists)
 
101
 *      then an error message is left in interp->result.
 
102
 *
 
103
 * Side effects:
 
104
 *      None.
 
105
 *
 
106
 *----------------------------------------------------------------------
 
107
 */
 
108
 
 
109
int
 
110
TkSelGetSelection(interp, tkwin, selection, target, proc, clientData)
 
111
    Tcl_Interp *interp;         /* Interpreter to use for reporting
 
112
                                 * errors. */
 
113
    Tk_Window tkwin;            /* Window on whose behalf to retrieve
 
114
                                 * the selection (determines display
 
115
                                 * from which to retrieve). */
 
116
    Atom selection;             /* Selection to retrieve. */
 
117
    Atom target;                /* Desired form in which selection
 
118
                                 * is to be returned. */
 
119
    Tk_GetSelProc *proc;        /* Procedure to call to process the
 
120
                                 * selection, once it has been retrieved. */
 
121
    ClientData clientData;      /* Arbitrary value to pass to proc. */
 
122
{
 
123
    TkSelRetrievalInfo retr;
 
124
    TkWindow *winPtr = (TkWindow *) tkwin;
 
125
    TkDisplay *dispPtr = winPtr->dispPtr;
 
126
 
 
127
    /*
 
128
     * The selection is owned by some other process.  To
 
129
     * retrieve it, first record information about the retrieval
 
130
     * in progress.  Use an internal window as the requestor.
 
131
     */
 
132
 
 
133
    retr.interp = interp;
 
134
    if (dispPtr->clipWindow == NULL) {
 
135
        int result;
 
136
 
 
137
        result = TkClipInit(interp, dispPtr);
 
138
        if (result != TCL_OK) {
 
139
            return result;
 
140
        }
 
141
    }
 
142
    retr.winPtr = (TkWindow *) dispPtr->clipWindow;
 
143
    retr.selection = selection;
 
144
    retr.property = selection;
 
145
    retr.target = target;
 
146
    retr.proc = proc;
 
147
    retr.clientData = clientData;
 
148
    retr.result = -1;
 
149
    retr.idleTime = 0;
 
150
    retr.nextPtr = pendingRetrievals;
 
151
    pendingRetrievals = &retr;
 
152
 
 
153
    /*
 
154
     * Initiate the request for the selection.  Note:  can't use
 
155
     * TkCurrentTime for the time.  If we do, and this application hasn't
 
156
     * received any X events in a long time, the current time will be way
 
157
     * in the past and could even predate the time when the selection was
 
158
     * made;  if this happens, the request will be rejected.
 
159
     */
 
160
 
 
161
    XConvertSelection(winPtr->display, retr.selection, retr.target,
 
162
            retr.property, retr.winPtr->window, CurrentTime);
 
163
 
 
164
    /*
 
165
     * Enter a loop processing X events until the selection
 
166
     * has been retrieved and processed.  If no response is
 
167
     * received within a few seconds, then timeout.
 
168
     */
 
169
 
 
170
    retr.timeout = Tcl_CreateTimerHandler(1000, SelTimeoutProc,
 
171
            (ClientData) &retr);
 
172
    while (retr.result == -1) {
 
173
        Tcl_DoOneEvent(0);
 
174
    }
 
175
    Tcl_DeleteTimerHandler(retr.timeout);
 
176
 
 
177
    /*
 
178
     * Unregister the information about the selection retrieval
 
179
     * in progress.
 
180
     */
 
181
 
 
182
    if (pendingRetrievals == &retr) {
 
183
        pendingRetrievals = retr.nextPtr;
 
184
    } else {
 
185
        TkSelRetrievalInfo *retrPtr;
 
186
 
 
187
        for (retrPtr = pendingRetrievals; retrPtr != NULL;
 
188
                retrPtr = retrPtr->nextPtr) {
 
189
            if (retrPtr->nextPtr == &retr) {
 
190
                retrPtr->nextPtr = retr.nextPtr;
 
191
                break;
 
192
            }
 
193
        }
 
194
    }
 
195
    return retr.result;
 
196
}
 
197
 
 
198
/*
 
199
 *----------------------------------------------------------------------
 
200
 *
 
201
 * TkSelPropProc --
 
202
 *
 
203
 *      This procedure is invoked when property-change events
 
204
 *      occur on windows not known to the toolkit.  Its function
 
205
 *      is to implement the sending side of the INCR selection
 
206
 *      retrieval protocol when the selection requestor deletes
 
207
 *      the property containing a part of the selection.
 
208
 *
 
209
 * Results:
 
210
 *      None.
 
211
 *
 
212
 * Side effects:
 
213
 *      If the property that is receiving the selection was just
 
214
 *      deleted, then a new piece of the selection is fetched and
 
215
 *      placed in the property, until eventually there's no more
 
216
 *      selection to fetch.
 
217
 *
 
218
 *----------------------------------------------------------------------
 
219
 */
 
220
 
 
221
void
 
222
TkSelPropProc(eventPtr)
 
223
    register XEvent *eventPtr;          /* X PropertyChange event. */
 
224
{
 
225
    register IncrInfo *incrPtr;
 
226
    int i, format;
 
227
    Atom target, formatType;
 
228
    register TkSelHandler *selPtr;
 
229
    long buffer[TK_SEL_WORDS_AT_ONCE];
 
230
    int numItems;
 
231
    char *propPtr;
 
232
    Tk_ErrorHandler errorHandler;
 
233
 
 
234
    /*
 
235
     * See if this event announces the deletion of a property being
 
236
     * used for an INCR transfer.  If so, then add the next chunk of
 
237
     * data to the property.
 
238
     */
 
239
 
 
240
    if (eventPtr->xproperty.state != PropertyDelete) {
 
241
        return;
 
242
    }
 
243
    for (incrPtr = pendingIncrs; incrPtr != NULL;
 
244
            incrPtr = incrPtr->nextPtr) {
 
245
        if (incrPtr->reqWindow != eventPtr->xproperty.window) {
 
246
            continue;
 
247
        }
 
248
        for (i = 0; i < incrPtr->numConversions; i++) {
 
249
            if ((eventPtr->xproperty.atom != incrPtr->multAtoms[2*i + 1])
 
250
                    || (incrPtr->offsets[i] == -1)){
 
251
                continue;
 
252
            }
 
253
            target = incrPtr->multAtoms[2*i];
 
254
            incrPtr->idleTime = 0;
 
255
            for (selPtr = incrPtr->winPtr->selHandlerList; ;
 
256
                    selPtr = selPtr->nextPtr) {
 
257
                if (selPtr == NULL) {
 
258
                    incrPtr->multAtoms[2*i + 1] = None;
 
259
                    incrPtr->offsets[i] = -1;
 
260
                    incrPtr->numIncrs --;
 
261
                    return;
 
262
                }
 
263
                if ((selPtr->target == target)
 
264
                        && (selPtr->selection == incrPtr->selection)) {
 
265
                    formatType = selPtr->format;
 
266
                    if (incrPtr->offsets[i] == -2) {
 
267
                        numItems = 0;
 
268
                        ((char *) buffer)[0] = 0;
 
269
                    } else {
 
270
                        TkSelInProgress ip;
 
271
                        ip.selPtr = selPtr;
 
272
                        ip.nextPtr = pendingPtr;
 
273
                        pendingPtr = &ip;
 
274
                        numItems = (*selPtr->proc)(selPtr->clientData,
 
275
                                incrPtr->offsets[i], (char *) buffer,
 
276
                                TK_SEL_BYTES_AT_ONCE);
 
277
                        pendingPtr = ip.nextPtr;
 
278
                        if (ip.selPtr == NULL) {
 
279
                            /*
 
280
                             * The selection handler deleted itself.
 
281
                             */
 
282
 
 
283
                            return;
 
284
                        }
 
285
                        if (numItems > TK_SEL_BYTES_AT_ONCE) {
 
286
                            panic("selection handler returned too many bytes");
 
287
                        } else {
 
288
                            if (numItems < 0) {
 
289
                                numItems = 0;
 
290
                            }
 
291
                        }
 
292
                        ((char *) buffer)[numItems] = '\0';
 
293
                    }
 
294
                    if (numItems < TK_SEL_BYTES_AT_ONCE) {
 
295
                        if (numItems <= 0) {
 
296
                            incrPtr->offsets[i] = -1;
 
297
                            incrPtr->numIncrs--;
 
298
                        } else {
 
299
                            incrPtr->offsets[i] = -2;
 
300
                        }
 
301
                    } else {
 
302
                        incrPtr->offsets[i] += numItems;
 
303
                    }
 
304
                    if (formatType == XA_STRING) {
 
305
                        propPtr = (char *) buffer;
 
306
                        format = 8;
 
307
                    } else {
 
308
                        propPtr = (char *) SelCvtToX((char *) buffer,
 
309
                                formatType, (Tk_Window) incrPtr->winPtr,
 
310
                                &numItems);
 
311
                        format = 32;
 
312
                    }
 
313
                    errorHandler = Tk_CreateErrorHandler(
 
314
                            eventPtr->xproperty.display, -1, -1, -1,
 
315
                            (int (*)()) NULL, (ClientData) NULL);
 
316
                    XChangeProperty(eventPtr->xproperty.display,
 
317
                            eventPtr->xproperty.window,
 
318
                            eventPtr->xproperty.atom, formatType,
 
319
                            format, PropModeReplace,
 
320
                            (unsigned char *) propPtr, numItems);
 
321
                    Tk_DeleteErrorHandler(errorHandler);
 
322
                    if (propPtr != (char *) buffer) {
 
323
                        ckfree(propPtr);
 
324
                    }
 
325
                    return;
 
326
                }
 
327
            }
 
328
        }
 
329
    }
 
330
}
 
331
 
 
332
/*
 
333
 *--------------------------------------------------------------
 
334
 *
 
335
 * TkSelEventProc --
 
336
 *
 
337
 *      This procedure is invoked whenever a selection-related
 
338
 *      event occurs.  It does the lion's share of the work
 
339
 *      in implementing the selection protocol.
 
340
 *
 
341
 * Results:
 
342
 *      None.
 
343
 *
 
344
 * Side effects:
 
345
 *      Lots:  depends on the type of event.
 
346
 *
 
347
 *--------------------------------------------------------------
 
348
 */
 
349
 
 
350
void
 
351
TkSelEventProc(tkwin, eventPtr)
 
352
    Tk_Window tkwin;            /* Window for which event was
 
353
                                 * targeted. */
 
354
    register XEvent *eventPtr;  /* X event:  either SelectionClear,
 
355
                                 * SelectionRequest, or
 
356
                                 * SelectionNotify. */
 
357
{
 
358
    register TkWindow *winPtr = (TkWindow *) tkwin;
 
359
    TkDisplay *dispPtr = winPtr->dispPtr;
 
360
    Tcl_Interp *interp;
 
361
 
 
362
    /*
 
363
     * Case #1: SelectionClear events.
 
364
     */
 
365
 
 
366
    if (eventPtr->type == SelectionClear) {
 
367
        TkSelClearSelection(tkwin, eventPtr);
 
368
    }
 
369
 
 
370
    /*
 
371
     * Case #2: SelectionNotify events.  Call the relevant procedure
 
372
     * to handle the incoming selection.
 
373
     */
 
374
 
 
375
    if (eventPtr->type == SelectionNotify) {
 
376
        register TkSelRetrievalInfo *retrPtr;
 
377
        char *propInfo;
 
378
        Atom type;
 
379
        int format, result;
 
380
        unsigned long numItems, bytesAfter;
 
381
 
 
382
        for (retrPtr = pendingRetrievals; ; retrPtr = retrPtr->nextPtr) {
 
383
            if (retrPtr == NULL) {
 
384
                return;
 
385
            }
 
386
            if ((retrPtr->winPtr == winPtr)
 
387
                    && (retrPtr->selection == eventPtr->xselection.selection)
 
388
                    && (retrPtr->target == eventPtr->xselection.target)
 
389
                    && (retrPtr->result == -1)) {
 
390
                if (retrPtr->property == eventPtr->xselection.property) {
 
391
                    break;
 
392
                }
 
393
                if (eventPtr->xselection.property == None) {
 
394
                    Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
 
395
                    Tcl_AppendResult(retrPtr->interp,
 
396
                            Tk_GetAtomName(tkwin, retrPtr->selection),
 
397
                            " selection doesn't exist or form \"",
 
398
                            Tk_GetAtomName(tkwin, retrPtr->target),
 
399
                            "\" not defined", (char *) NULL);
 
400
                    retrPtr->result = TCL_ERROR;
 
401
                    return;
 
402
                }
 
403
            }
 
404
        }
 
405
 
 
406
        propInfo = NULL;
 
407
        result = XGetWindowProperty(eventPtr->xselection.display,
 
408
                eventPtr->xselection.requestor, retrPtr->property,
 
409
                0, MAX_PROP_WORDS, False, (Atom) AnyPropertyType,
 
410
                &type, &format, &numItems, &bytesAfter,
 
411
                (unsigned char **) &propInfo);
 
412
        if ((result != Success) || (type == None)) {
 
413
            return;
 
414
        }
 
415
        if (bytesAfter != 0) {
 
416
            Tcl_SetResult(retrPtr->interp, "selection property too large",
 
417
                TCL_STATIC);
 
418
            retrPtr->result = TCL_ERROR;
 
419
            XFree(propInfo);
 
420
            return;
 
421
        }
 
422
        if ((type == XA_STRING) || (type == dispPtr->textAtom)
 
423
                || (type == dispPtr->compoundTextAtom)) {
 
424
            if (format != 8) {
 
425
                sprintf(retrPtr->interp->result,
 
426
                    "bad format for string selection: wanted \"8\", got \"%d\"",
 
427
                    format);
 
428
                retrPtr->result = TCL_ERROR;
 
429
                return;
 
430
            }
 
431
            interp = retrPtr->interp;
 
432
            Tcl_Preserve((ClientData) interp);
 
433
            retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
 
434
                    interp, propInfo);
 
435
            Tcl_Release((ClientData) interp);
 
436
        } else if (type == dispPtr->incrAtom) {
 
437
 
 
438
            /*
 
439
             * It's a !?#@!?!! INCR-style reception.  Arrange to receive
 
440
             * the selection in pieces, using the ICCCM protocol, then
 
441
             * hang around until either the selection is all here or a
 
442
             * timeout occurs.
 
443
             */
 
444
 
 
445
            retrPtr->idleTime = 0;
 
446
            Tk_CreateEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
 
447
                    (ClientData) retrPtr);
 
448
            XDeleteProperty(Tk_Display(tkwin), Tk_WindowId(tkwin),
 
449
                    retrPtr->property);
 
450
            while (retrPtr->result == -1) {
 
451
                Tcl_DoOneEvent(0);
 
452
            }
 
453
            Tk_DeleteEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
 
454
                    (ClientData) retrPtr);
 
455
        } else {
 
456
            char *string;
 
457
 
 
458
            if (format != 32) {
 
459
                sprintf(retrPtr->interp->result,
 
460
                    "bad format for selection: wanted \"32\", got \"%d\"",
 
461
                    format);
 
462
                retrPtr->result = TCL_ERROR;
 
463
                return;
 
464
            }
 
465
            string = SelCvtFromX((long *) propInfo, (int) numItems, type,
 
466
                    (Tk_Window) winPtr);
 
467
            interp = retrPtr->interp;
 
468
            Tcl_Preserve((ClientData) interp);
 
469
            retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
 
470
                    interp, string);
 
471
            Tcl_Release((ClientData) interp);
 
472
            ckfree(string);
 
473
        }
 
474
        XFree(propInfo);
 
475
        return;
 
476
    }
 
477
 
 
478
    /*
 
479
     * Case #3: SelectionRequest events.  Call ConvertSelection to
 
480
     * do the dirty work.
 
481
     */
 
482
 
 
483
    if (eventPtr->type == SelectionRequest) {
 
484
        ConvertSelection(winPtr, &eventPtr->xselectionrequest);
 
485
        return;
 
486
    }
 
487
}
 
488
 
 
489
/*
 
490
 *----------------------------------------------------------------------
 
491
 *
 
492
 * SelTimeoutProc --
 
493
 *
 
494
 *      This procedure is invoked once every second while waiting for
 
495
 *      the selection to be returned.  After a while it gives up and
 
496
 *      aborts the selection retrieval.
 
497
 *
 
498
 * Results:
 
499
 *      None.
 
500
 *
 
501
 * Side effects:
 
502
 *      A new timer callback is created to call us again in another
 
503
 *      second, unless time has expired, in which case an error is
 
504
 *      recorded for the retrieval.
 
505
 *
 
506
 *----------------------------------------------------------------------
 
507
 */
 
508
 
 
509
static void
 
510
SelTimeoutProc(clientData)
 
511
    ClientData clientData;              /* Information about retrieval
 
512
                                         * in progress. */
 
513
{
 
514
    register TkSelRetrievalInfo *retrPtr = (TkSelRetrievalInfo *) clientData;
 
515
 
 
516
    /*
 
517
     * Make sure that the retrieval is still in progress.  Then
 
518
     * see how long it's been since any sort of response was received
 
519
     * from the other side.
 
520
     */
 
521
 
 
522
    if (retrPtr->result != -1) {
 
523
        return;
 
524
    }
 
525
    retrPtr->idleTime++;
 
526
    if (retrPtr->idleTime >= 5) {
 
527
 
 
528
        /*
 
529
         * Use a careful procedure to store the error message, because
 
530
         * the result could already be partially filled in with a partial
 
531
         * selection return.
 
532
         */
 
533
 
 
534
        Tcl_SetResult(retrPtr->interp, "selection owner didn't respond",
 
535
                TCL_STATIC);
 
536
        retrPtr->result = TCL_ERROR;
 
537
    } else {
 
538
        retrPtr->timeout = Tcl_CreateTimerHandler(1000, SelTimeoutProc,
 
539
            (ClientData) retrPtr);
 
540
    }
 
541
}
 
542
 
 
543
/*
 
544
 *----------------------------------------------------------------------
 
545
 *
 
546
 * ConvertSelection --
 
547
 *
 
548
 *      This procedure is invoked to handle SelectionRequest events.
 
549
 *      It responds to the requests, obeying the ICCCM protocols.
 
550
 *
 
551
 * Results:
 
552
 *      None.
 
553
 *
 
554
 * Side effects:
 
555
 *      Properties are created for the selection requestor, and a
 
556
 *      SelectionNotify event is generated for the selection
 
557
 *      requestor.  In the event of long selections, this procedure
 
558
 *      implements INCR-mode transfers, using the ICCCM protocol.
 
559
 *
 
560
 *----------------------------------------------------------------------
 
561
 */
 
562
 
 
563
static void
 
564
ConvertSelection(winPtr, eventPtr)
 
565
    TkWindow *winPtr;                   /* Window that received the
 
566
                                         * conversion request;  may not be
 
567
                                         * selection's current owner, be we
 
568
                                         * set it to the current owner. */
 
569
    register XSelectionRequestEvent *eventPtr;
 
570
                                        /* Event describing request. */
 
571
{
 
572
    XSelectionEvent reply;              /* Used to notify requestor that
 
573
                                         * selection info is ready. */
 
574
    int multiple;                       /* Non-zero means a MULTIPLE request
 
575
                                         * is being handled. */
 
576
    IncrInfo incr;                      /* State of selection conversion. */
 
577
    Atom singleInfo[2];                 /* incr.multAtoms points here except
 
578
                                         * for multiple conversions. */
 
579
    int i;
 
580
    Tk_ErrorHandler errorHandler;
 
581
    TkSelectionInfo *infoPtr;
 
582
    TkSelInProgress ip;
 
583
 
 
584
    errorHandler = Tk_CreateErrorHandler(eventPtr->display, -1, -1,-1,
 
585
            (int (*)()) NULL, (ClientData) NULL);
 
586
 
 
587
    /*
 
588
     * Initialize the reply event.
 
589
     */
 
590
 
 
591
    reply.type = SelectionNotify;
 
592
    reply.serial = 0;
 
593
    reply.send_event = True;
 
594
    reply.display = eventPtr->display;
 
595
    reply.requestor = eventPtr->requestor;
 
596
    reply.selection = eventPtr->selection;
 
597
    reply.target = eventPtr->target;
 
598
    reply.property = eventPtr->property;
 
599
    if (reply.property == None) {
 
600
        reply.property = reply.target;
 
601
    }
 
602
    reply.time = eventPtr->time;
 
603
 
 
604
    for (infoPtr = winPtr->dispPtr->selectionInfoPtr; infoPtr != NULL;
 
605
            infoPtr = infoPtr->nextPtr) {
 
606
        if (infoPtr->selection == eventPtr->selection)
 
607
            break;
 
608
    }
 
609
    if (infoPtr == NULL) {
 
610
        goto refuse;
 
611
    }
 
612
    winPtr = (TkWindow *) infoPtr->owner;
 
613
 
 
614
    /*
 
615
     * Figure out which kind(s) of conversion to perform.  If handling
 
616
     * a MULTIPLE conversion, then read the property describing which
 
617
     * conversions to perform.
 
618
     */
 
619
 
 
620
    incr.winPtr = winPtr;
 
621
    incr.selection = eventPtr->selection;
 
622
    if (eventPtr->target != winPtr->dispPtr->multipleAtom) {
 
623
        multiple = 0;
 
624
        singleInfo[0] = reply.target;
 
625
        singleInfo[1] = reply.property;
 
626
        incr.multAtoms = singleInfo;
 
627
        incr.numConversions = 1;
 
628
    } else {
 
629
        Atom type;
 
630
        int format, result;
 
631
        unsigned long bytesAfter;
 
632
 
 
633
        multiple = 1;
 
634
        incr.multAtoms = NULL;
 
635
        if (eventPtr->property == None) {
 
636
            goto refuse;
 
637
        }
 
638
        result = XGetWindowProperty(eventPtr->display,
 
639
                eventPtr->requestor, eventPtr->property,
 
640
                0, MAX_PROP_WORDS, False, XA_ATOM,
 
641
                &type, &format, &incr.numConversions, &bytesAfter,
 
642
                (unsigned char **) &incr.multAtoms);
 
643
        if ((result != Success) || (bytesAfter != 0) || (format != 32)
 
644
                || (type == None)) {
 
645
            if (incr.multAtoms != NULL) {
 
646
                XFree((char *) incr.multAtoms);
 
647
            }
 
648
            goto refuse;
 
649
        }
 
650
        incr.numConversions /= 2;               /* Two atoms per conversion. */
 
651
    }
 
652
 
 
653
    /*
 
654
     * Loop through all of the requested conversions, and either return
 
655
     * the entire converted selection, if it can be returned in a single
 
656
     * bunch, or return INCR information only (the actual selection will
 
657
     * be returned below).
 
658
     */
 
659
 
 
660
    incr.offsets = (int *) ckalloc((unsigned)
 
661
            (incr.numConversions*sizeof(int)));
 
662
    incr.numIncrs = 0;
 
663
    for (i = 0; i < incr.numConversions; i++) {
 
664
        Atom target, property, type;
 
665
        long buffer[TK_SEL_WORDS_AT_ONCE];
 
666
        register TkSelHandler *selPtr;
 
667
        int numItems, format;
 
668
        char *propPtr;
 
669
 
 
670
        target = incr.multAtoms[2*i];
 
671
        property = incr.multAtoms[2*i + 1];
 
672
        incr.offsets[i] = -1;
 
673
 
 
674
        for (selPtr = winPtr->selHandlerList; selPtr != NULL;
 
675
                selPtr = selPtr->nextPtr) {
 
676
            if ((selPtr->target == target)
 
677
                    && (selPtr->selection == eventPtr->selection)) {
 
678
                break;
 
679
            }
 
680
        }
 
681
 
 
682
        if (selPtr == NULL) {
 
683
            /*
 
684
             * Nobody seems to know about this kind of request.  If
 
685
             * it's of a sort that we can handle without any help, do
 
686
             * it.  Otherwise mark the request as an errror.
 
687
             */
 
688
 
 
689
            numItems = TkSelDefaultSelection(infoPtr, target, (char *) buffer,
 
690
                    TK_SEL_BYTES_AT_ONCE, &type);
 
691
            if (numItems < 0) {
 
692
                incr.multAtoms[2*i + 1] = None;
 
693
                continue;
 
694
            }
 
695
        } else {
 
696
            ip.selPtr = selPtr;
 
697
            ip.nextPtr = pendingPtr;
 
698
            pendingPtr = &ip;
 
699
            type = selPtr->format;
 
700
            numItems = (*selPtr->proc)(selPtr->clientData, 0,
 
701
                    (char *) buffer, TK_SEL_BYTES_AT_ONCE);
 
702
            pendingPtr = ip.nextPtr;
 
703
            if ((ip.selPtr == NULL) || (numItems < 0)) {
 
704
                incr.multAtoms[2*i + 1] = None;
 
705
                continue;
 
706
            }
 
707
            if (numItems > TK_SEL_BYTES_AT_ONCE) {
 
708
                panic("selection handler returned too many bytes");
 
709
            }
 
710
            ((char *) buffer)[numItems] = '\0';
 
711
        }
 
712
 
 
713
        /*
 
714
         * Got the selection;  store it back on the requestor's property.
 
715
         */
 
716
 
 
717
        if (numItems == TK_SEL_BYTES_AT_ONCE) {
 
718
            /*
 
719
             * Selection is too big to send at once;  start an
 
720
             * INCR-mode transfer.
 
721
             */
 
722
 
 
723
            incr.numIncrs++;
 
724
            type = winPtr->dispPtr->incrAtom;
 
725
            buffer[0] = SelectionSize(selPtr);
 
726
            if (buffer[0] == 0) {
 
727
                incr.multAtoms[2*i + 1] = None;
 
728
                continue;
 
729
            }
 
730
            numItems = 1;
 
731
            propPtr = (char *) buffer;
 
732
            format = 32;
 
733
            incr.offsets[i] = 0;
 
734
        } else if (type == XA_STRING) {
 
735
            propPtr = (char *) buffer;
 
736
            format = 8;
 
737
        } else {
 
738
            propPtr = (char *) SelCvtToX((char *) buffer,
 
739
                    type, (Tk_Window) winPtr, &numItems);
 
740
            format = 32;
 
741
        }
 
742
        XChangeProperty(reply.display, reply.requestor,
 
743
                property, type, format, PropModeReplace,
 
744
                (unsigned char *) propPtr, numItems);
 
745
        if (propPtr != (char *) buffer) {
 
746
            ckfree(propPtr);
 
747
        }
 
748
    }
 
749
 
 
750
    /*
 
751
     * Send an event back to the requestor to indicate that the
 
752
     * first stage of conversion is complete (everything is done
 
753
     * except for long conversions that have to be done in INCR
 
754
     * mode).
 
755
     */
 
756
 
 
757
    if (incr.numIncrs > 0) {
 
758
        XSelectInput(reply.display, reply.requestor, PropertyChangeMask);
 
759
        incr.timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc,
 
760
            (ClientData) &incr);
 
761
        incr.idleTime = 0;
 
762
        incr.reqWindow = reply.requestor;
 
763
        incr.time = infoPtr->time;
 
764
        incr.nextPtr = pendingIncrs;
 
765
        pendingIncrs = &incr;
 
766
    }
 
767
    if (multiple) {
 
768
        XChangeProperty(reply.display, reply.requestor, reply.property,
 
769
                XA_ATOM, 32, PropModeReplace,
 
770
                (unsigned char *) incr.multAtoms,
 
771
                (int) incr.numConversions*2);
 
772
    } else {
 
773
 
 
774
        /*
 
775
         * Not a MULTIPLE request.  The first property in "multAtoms"
 
776
         * got set to None if there was an error in conversion.
 
777
         */
 
778
 
 
779
        reply.property = incr.multAtoms[1];
 
780
    }
 
781
    XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
 
782
    Tk_DeleteErrorHandler(errorHandler);
 
783
 
 
784
    /*
 
785
     * Handle any remaining INCR-mode transfers.  This all happens
 
786
     * in callbacks to TkSelPropProc, so just wait until the number
 
787
     * of uncompleted INCR transfers drops to zero.
 
788
     */
 
789
 
 
790
    if (incr.numIncrs > 0) {
 
791
        IncrInfo *incrPtr2;
 
792
 
 
793
        while (incr.numIncrs > 0) {
 
794
            Tcl_DoOneEvent(0);
 
795
        }
 
796
        Tcl_DeleteTimerHandler(incr.timeout);
 
797
        errorHandler = Tk_CreateErrorHandler(winPtr->display,
 
798
                -1, -1,-1, (int (*)()) NULL, (ClientData) NULL);
 
799
        XSelectInput(reply.display, reply.requestor, 0L);
 
800
        Tk_DeleteErrorHandler(errorHandler);
 
801
        if (pendingIncrs == &incr) {
 
802
            pendingIncrs = incr.nextPtr;
 
803
        } else {
 
804
            for (incrPtr2 = pendingIncrs; incrPtr2 != NULL;
 
805
                    incrPtr2 = incrPtr2->nextPtr) {
 
806
                if (incrPtr2->nextPtr == &incr) {
 
807
                    incrPtr2->nextPtr = incr.nextPtr;
 
808
                    break;
 
809
                }
 
810
            }
 
811
        }
 
812
    }
 
813
 
 
814
    /*
 
815
     * All done.  Cleanup and return.
 
816
     */
 
817
 
 
818
    ckfree((char *) incr.offsets);
 
819
    if (multiple) {
 
820
        XFree((char *) incr.multAtoms);
 
821
    }
 
822
    return;
 
823
 
 
824
    /*
 
825
     * An error occurred.  Send back a refusal message.
 
826
     */
 
827
 
 
828
    refuse:
 
829
    reply.property = None;
 
830
    XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
 
831
    Tk_DeleteErrorHandler(errorHandler);
 
832
    return;
 
833
}
 
834
 
 
835
/*
 
836
 *----------------------------------------------------------------------
 
837
 *
 
838
 * SelRcvIncrProc --
 
839
 *
 
840
 *      This procedure handles the INCR protocol on the receiving
 
841
 *      side.  It is invoked in response to property changes on
 
842
 *      the requestor's window (which hopefully are because a new
 
843
 *      chunk of the selection arrived).
 
844
 *
 
845
 * Results:
 
846
 *      None.
 
847
 *
 
848
 * Side effects:
 
849
 *      If a new piece of selection has arrived, a procedure is
 
850
 *      invoked to deal with that piece.  When the whole selection
 
851
 *      is here, a flag is left for the higher-level procedure that
 
852
 *      initiated the selection retrieval.
 
853
 *
 
854
 *----------------------------------------------------------------------
 
855
 */
 
856
 
 
857
static void
 
858
SelRcvIncrProc(clientData, eventPtr)
 
859
    ClientData clientData;              /* Information about retrieval. */
 
860
    register XEvent *eventPtr;          /* X PropertyChange event. */
 
861
{
 
862
    register TkSelRetrievalInfo *retrPtr = (TkSelRetrievalInfo *) clientData;
 
863
    char *propInfo;
 
864
    Atom type;
 
865
    int format, result;
 
866
    unsigned long numItems, bytesAfter;
 
867
    Tcl_Interp *interp;
 
868
 
 
869
    if ((eventPtr->xproperty.atom != retrPtr->property)
 
870
            || (eventPtr->xproperty.state != PropertyNewValue)
 
871
            || (retrPtr->result != -1)) {
 
872
        return;
 
873
    }
 
874
    propInfo = NULL;
 
875
    result = XGetWindowProperty(eventPtr->xproperty.display,
 
876
            eventPtr->xproperty.window, retrPtr->property, 0, MAX_PROP_WORDS,
 
877
            True, (Atom) AnyPropertyType, &type, &format, &numItems,
 
878
            &bytesAfter, (unsigned char **) &propInfo);
 
879
    if ((result != Success) || (type == None)) {
 
880
        return;
 
881
    }
 
882
    if (bytesAfter != 0) {
 
883
        Tcl_SetResult(retrPtr->interp, "selection property too large",
 
884
                TCL_STATIC);
 
885
        retrPtr->result = TCL_ERROR;
 
886
        goto done;
 
887
    }
 
888
    if (numItems == 0) {
 
889
        retrPtr->result = TCL_OK;
 
890
    } else if ((type == XA_STRING)
 
891
            || (type == retrPtr->winPtr->dispPtr->textAtom)
 
892
            || (type == retrPtr->winPtr->dispPtr->compoundTextAtom)) {
 
893
        if (format != 8) {
 
894
            Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
 
895
            sprintf(retrPtr->interp->result,
 
896
                "bad format for string selection: wanted \"8\", got \"%d\"",
 
897
                format);
 
898
            retrPtr->result = TCL_ERROR;
 
899
            goto done;
 
900
        }
 
901
        interp = retrPtr->interp;
 
902
        Tcl_Preserve((ClientData) interp);
 
903
        result = (*retrPtr->proc)(retrPtr->clientData, interp, propInfo);
 
904
        Tcl_Release((ClientData) interp);
 
905
        if (result != TCL_OK) {
 
906
            retrPtr->result = result;
 
907
        }
 
908
    } else {
 
909
        char *string;
 
910
 
 
911
        if (format != 32) {
 
912
            Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
 
913
            sprintf(retrPtr->interp->result,
 
914
                "bad format for selection: wanted \"32\", got \"%d\"",
 
915
                format);
 
916
            retrPtr->result = TCL_ERROR;
 
917
            goto done;
 
918
        }
 
919
        string = SelCvtFromX((long *) propInfo, (int) numItems, type,
 
920
                (Tk_Window) retrPtr->winPtr);
 
921
        interp = retrPtr->interp;
 
922
        Tcl_Preserve((ClientData) interp);
 
923
        result = (*retrPtr->proc)(retrPtr->clientData, interp, string);
 
924
        Tcl_Release((ClientData) interp);
 
925
        if (result != TCL_OK) {
 
926
            retrPtr->result = result;
 
927
        }
 
928
        ckfree(string);
 
929
    }
 
930
 
 
931
    done:
 
932
    XFree(propInfo);
 
933
    retrPtr->idleTime = 0;
 
934
}
 
935
 
 
936
/*
 
937
 *----------------------------------------------------------------------
 
938
 *
 
939
 * SelectionSize --
 
940
 *
 
941
 *      This procedure is called when the selection is too large to
 
942
 *      send in a single buffer;  it computes the total length of
 
943
 *      the selection in bytes.
 
944
 *
 
945
 * Results:
 
946
 *      The return value is the number of bytes in the selection
 
947
 *      given by selPtr.
 
948
 *
 
949
 * Side effects:
 
950
 *      The selection is retrieved from its current owner (this is
 
951
 *      the only way to compute its size).
 
952
 *
 
953
 *----------------------------------------------------------------------
 
954
 */
 
955
 
 
956
static int
 
957
SelectionSize(selPtr)
 
958
    TkSelHandler *selPtr;       /* Information about how to retrieve
 
959
                                 * the selection whose size is wanted. */
 
960
{
 
961
    char buffer[TK_SEL_BYTES_AT_ONCE+1];
 
962
    int size, chunkSize;
 
963
    TkSelInProgress ip;
 
964
 
 
965
    size = TK_SEL_BYTES_AT_ONCE;
 
966
    ip.selPtr = selPtr;
 
967
    ip.nextPtr = pendingPtr;
 
968
    pendingPtr = &ip;
 
969
    do {
 
970
        chunkSize = (*selPtr->proc)(selPtr->clientData, size,
 
971
                        (char *) buffer, TK_SEL_BYTES_AT_ONCE);
 
972
        if (ip.selPtr == NULL) {
 
973
            size = 0;
 
974
            break;
 
975
        }
 
976
        size += chunkSize;
 
977
    } while (chunkSize == TK_SEL_BYTES_AT_ONCE);
 
978
    pendingPtr = ip.nextPtr;
 
979
    return size;
 
980
}
 
981
 
 
982
/*
 
983
 *----------------------------------------------------------------------
 
984
 *
 
985
 * IncrTimeoutProc --
 
986
 *
 
987
 *      This procedure is invoked once a second while sending the
 
988
 *      selection to a requestor in INCR mode.  After a while it
 
989
 *      gives up and aborts the selection operation.
 
990
 *
 
991
 * Results:
 
992
 *      None.
 
993
 *
 
994
 * Side effects:
 
995
 *      A new timeout gets registered so that this procedure gets
 
996
 *      called again in another second, unless too many seconds
 
997
 *      have elapsed, in which case incrPtr is marked as "all done".
 
998
 *
 
999
 *----------------------------------------------------------------------
 
1000
 */
 
1001
 
 
1002
static void
 
1003
IncrTimeoutProc(clientData)
 
1004
    ClientData clientData;              /* Information about INCR-mode
 
1005
                                         * selection retrieval for which
 
1006
                                         * we are selection owner. */
 
1007
{
 
1008
    register IncrInfo *incrPtr = (IncrInfo *) clientData;
 
1009
 
 
1010
    incrPtr->idleTime++;
 
1011
    if (incrPtr->idleTime >= 5) {
 
1012
        incrPtr->numIncrs = 0;
 
1013
    } else {
 
1014
        incrPtr->timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc,
 
1015
                (ClientData) incrPtr);
 
1016
    }
 
1017
}
 
1018
 
 
1019
/*
 
1020
 *----------------------------------------------------------------------
 
1021
 *
 
1022
 * SelCvtToX --
 
1023
 *
 
1024
 *      Given a selection represented as a string (the normal Tcl form),
 
1025
 *      convert it to the ICCCM-mandated format for X, depending on
 
1026
 *      the type argument.  This procedure and SelCvtFromX are inverses.
 
1027
 *
 
1028
 * Results:
 
1029
 *      The return value is a malloc'ed buffer holding a value
 
1030
 *      equivalent to "string", but formatted as for "type".  It is
 
1031
 *      the caller's responsibility to free the string when done with
 
1032
 *      it.  The word at *numLongsPtr is filled in with the number of
 
1033
 *      32-bit words returned in the result.
 
1034
 *
 
1035
 * Side effects:
 
1036
 *      None.
 
1037
 *
 
1038
 *----------------------------------------------------------------------
 
1039
 */
 
1040
 
 
1041
static long *
 
1042
SelCvtToX(string, type, tkwin, numLongsPtr)
 
1043
    char *string;               /* String representation of selection. */
 
1044
    Atom type;                  /* Atom specifying the X format that is
 
1045
                                 * desired for the selection.  Should not
 
1046
                                 * be XA_STRING (if so, don't bother calling
 
1047
                                 * this procedure at all). */
 
1048
    Tk_Window tkwin;            /* Window that governs atom conversion. */
 
1049
    int *numLongsPtr;           /* Number of 32-bit words contained in the
 
1050
                                 * result. */
 
1051
{
 
1052
    register char *p;
 
1053
    char *field;
 
1054
    int numFields;
 
1055
    long *propPtr, *longPtr;
 
1056
#define MAX_ATOM_NAME_LENGTH 100
 
1057
    char atomName[MAX_ATOM_NAME_LENGTH+1];
 
1058
 
 
1059
    /*
 
1060
     * The string is assumed to consist of fields separated by spaces.
 
1061
     * The property gets generated by converting each field to an
 
1062
     * integer number, in one of two ways:
 
1063
     * 1. If type is XA_ATOM, convert each field to its corresponding
 
1064
     *    atom.
 
1065
     * 2. If type is anything else, convert each field from an ASCII number
 
1066
     *    to a 32-bit binary number.
 
1067
     */
 
1068
 
 
1069
    numFields = 1;
 
1070
    for (p = string; *p != 0; p++) {
 
1071
        if (isspace(UCHAR(*p))) {
 
1072
            numFields++;
 
1073
        }
 
1074
    }
 
1075
    propPtr = (long *) ckalloc((unsigned) numFields*sizeof(long));
 
1076
 
 
1077
    /*
 
1078
     * Convert the fields one-by-one.
 
1079
     */
 
1080
 
 
1081
    for (longPtr = propPtr, *numLongsPtr = 0, p = string;
 
1082
            ; longPtr++, (*numLongsPtr)++) {
 
1083
        while (isspace(UCHAR(*p))) {
 
1084
            p++;
 
1085
        }
 
1086
        if (*p == 0) {
 
1087
            break;
 
1088
        }
 
1089
        field = p;
 
1090
        while ((*p != 0) && !isspace(UCHAR(*p))) {
 
1091
            p++;
 
1092
        }
 
1093
        if (type == XA_ATOM) {
 
1094
            int length;
 
1095
 
 
1096
            length = p - field;
 
1097
            if (length > MAX_ATOM_NAME_LENGTH) {
 
1098
                length = MAX_ATOM_NAME_LENGTH;
 
1099
            }
 
1100
            strncpy(atomName, field, (unsigned) length);
 
1101
            atomName[length] = 0;
 
1102
            *longPtr = (long) Tk_InternAtom(tkwin, atomName);
 
1103
        } else {
 
1104
            char *dummy;
 
1105
 
 
1106
            *longPtr = strtol(field, &dummy, 0);
 
1107
        }
 
1108
    }
 
1109
    return propPtr;
 
1110
}
 
1111
 
 
1112
/*
 
1113
 *----------------------------------------------------------------------
 
1114
 *
 
1115
 * SelCvtFromX --
 
1116
 *
 
1117
 *      Given an X property value, formatted as a collection of 32-bit
 
1118
 *      values according to "type" and the ICCCM conventions, convert
 
1119
 *      the value to a string suitable for manipulation by Tcl.  This
 
1120
 *      procedure is the inverse of SelCvtToX.
 
1121
 *
 
1122
 * Results:
 
1123
 *      The return value is the string equivalent of "property".  It is
 
1124
 *      malloc-ed and should be freed by the caller when no longer
 
1125
 *      needed.
 
1126
 *
 
1127
 * Side effects:
 
1128
 *      None.
 
1129
 *
 
1130
 *----------------------------------------------------------------------
 
1131
 */
 
1132
 
 
1133
static char *
 
1134
SelCvtFromX(propPtr, numValues, type, tkwin)
 
1135
    register long *propPtr;     /* Property value from X. */
 
1136
    int numValues;              /* Number of 32-bit values in property. */
 
1137
    Atom type;                  /* Type of property  Should not be
 
1138
                                 * XA_STRING (if so, don't bother calling
 
1139
                                 * this procedure at all). */
 
1140
    Tk_Window tkwin;            /* Window to use for atom conversion. */
 
1141
{
 
1142
    char *result;
 
1143
    int resultSpace, curSize, fieldSize;
 
1144
    char *atomName;
 
1145
 
 
1146
    /*
 
1147
     * Convert each long in the property to a string value, which is
 
1148
     * either the name of an atom (if type is XA_ATOM) or a hexadecimal
 
1149
     * string.  Make an initial guess about the size of the result, but
 
1150
     * be prepared to enlarge the result if necessary.
 
1151
     */
 
1152
 
 
1153
    resultSpace = 12*numValues+1;
 
1154
    curSize = 0;
 
1155
    atomName = "";      /* Not needed, but eliminates compiler warning. */
 
1156
    result = (char *) ckalloc((unsigned) resultSpace);
 
1157
    *result  = '\0';
 
1158
    for ( ; numValues > 0; propPtr++, numValues--) {
 
1159
        if (type == XA_ATOM) {
 
1160
            atomName = Tk_GetAtomName(tkwin, (Atom) *propPtr);
 
1161
            fieldSize = strlen(atomName) + 1;
 
1162
        } else {
 
1163
            fieldSize = 12;
 
1164
        }
 
1165
        if (curSize+fieldSize >= resultSpace) {
 
1166
            char *newResult;
 
1167
 
 
1168
            resultSpace *= 2;
 
1169
            if (curSize+fieldSize >= resultSpace) {
 
1170
                resultSpace = curSize + fieldSize + 1;
 
1171
            }
 
1172
            newResult = (char *) ckalloc((unsigned) resultSpace);
 
1173
            strncpy(newResult, result, (unsigned) curSize);
 
1174
            ckfree(result);
 
1175
            result = newResult;
 
1176
        }
 
1177
        if (curSize != 0) {
 
1178
            result[curSize] = ' ';
 
1179
            curSize++;
 
1180
        }
 
1181
        if (type == XA_ATOM) {
 
1182
            strcpy(result+curSize, atomName);
 
1183
        } else {
 
1184
            sprintf(result+curSize, "0x%x", (unsigned int) *propPtr);
 
1185
        }
 
1186
        curSize += strlen(result+curSize);
 
1187
    }
 
1188
    return result;
 
1189
}