1 /***********************************************************
2 Copyright (c) 1993, Oracle and/or its affiliates. All rights reserved.
3 
4 Permission is hereby granted, free of charge, to any person obtaining a
5 copy of this software and associated documentation files (the "Software"),
6 to deal in the Software without restriction, including without limitation
7 the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 and/or sell copies of the Software, and to permit persons to whom the
9 Software is furnished to do so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice (including the next
12 paragraph) shall be included in all copies or substantial portions of the
13 Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 DEALINGS IN THE SOFTWARE.
22 
23 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
24 
25                         All Rights Reserved
26 
27 Permission to use, copy, modify, and distribute this software and its
28 documentation for any purpose and without fee is hereby granted,
29 provided that the above copyright notice appear in all copies and that
30 both that copyright notice and this permission notice appear in
31 supporting documentation, and that the name of Digital not be
32 used in advertising or publicity pertaining to distribution of the
33 software without specific, written prior permission.
34 
35 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
41 SOFTWARE.
42 
43 ******************************************************************/
44 
45 /*
46 
47 Copyright 1987, 1988, 1994, 1998  The Open Group
48 
49 Permission to use, copy, modify, distribute, and sell this software and its
50 documentation for any purpose is hereby granted without fee, provided that
51 the above copyright notice appear in all copies and that both that
52 copyright notice and this permission notice appear in supporting
53 documentation.
54 
55 The above copyright notice and this permission notice shall be included in
56 all copies or substantial portions of the Software.
57 
58 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
61 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
62 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
63 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64 
65 Except as contained in this notice, the name of The Open Group shall not be
66 used in advertising or otherwise to promote the sale, use or other dealings
67 in this Software without prior written authorization from The Open Group.
68 
69 */
70 
71 #ifdef HAVE_CONFIG_H
72 #include <config.h>
73 #endif
74 #include "IntrinsicI.h"
75 #include "StringDefs.h"
76 #include "SelectionI.h"
77 #include <X11/Xatom.h>
78 #include <stdio.h>
79 
80 void
_XtSetDefaultSelectionTimeout(unsigned long * timeout)81 _XtSetDefaultSelectionTimeout(unsigned long *timeout)
82 {
83     *timeout = 5000;            /* default to 5 seconds */
84 }
85 
86 void
XtSetSelectionTimeout(unsigned long timeout)87 XtSetSelectionTimeout(unsigned long timeout)
88 {
89     XtAppSetSelectionTimeout(_XtDefaultAppContext(), timeout);
90 }
91 
92 void
XtAppSetSelectionTimeout(XtAppContext app,unsigned long timeout)93 XtAppSetSelectionTimeout(XtAppContext app, unsigned long timeout)
94 {
95     LOCK_APP(app);
96     app->selectionTimeout = timeout;
97     UNLOCK_APP(app);
98 }
99 
100 unsigned long
XtGetSelectionTimeout(void)101 XtGetSelectionTimeout(void)
102 {
103     return XtAppGetSelectionTimeout(_XtDefaultAppContext());
104 }
105 
106 unsigned long
XtAppGetSelectionTimeout(XtAppContext app)107 XtAppGetSelectionTimeout(XtAppContext app)
108 {
109     unsigned long retval;
110 
111     LOCK_APP(app);
112     retval = app->selectionTimeout;
113     UNLOCK_APP(app);
114     return retval;
115 }
116 
117 /* General utilities */
118 
119 static void HandleSelectionReplies(Widget, XtPointer, XEvent *, Boolean *);
120 static void ReqTimedOut(XtPointer, XtIntervalId *);
121 static void HandlePropertyGone(Widget, XtPointer, XEvent *, Boolean *);
122 static void HandleGetIncrement(Widget, XtPointer, XEvent *, Boolean *);
123 static void HandleIncremental(Display *, Widget, Atom, CallBackInfo,
124                               unsigned long);
125 
126 static XContext selectPropertyContext = 0;
127 static XContext paramPropertyContext = 0;
128 static XContext multipleContext = 0;
129 
130 /* Multiple utilities */
131 static void AddSelectionRequests(Widget, Atom, int, Atom *,
132                                  XtSelectionCallbackProc *, int, XtPointer *,
133                                  Boolean *, Atom *);
134 static Boolean IsGatheringRequest(Widget, Atom);
135 
136 #define PREALLOCED 32
137 
138 /* Parameter utilities */
139 static void AddParamInfo(Widget, Atom, Atom);
140 static void RemoveParamInfo(Widget, Atom);
141 static Atom GetParamInfo(Widget, Atom);
142 
143 static int StorageSize[3] = { 1, sizeof(short), sizeof(long) };
144 
145 #define BYTELENGTH(length, format) ((length) * (size_t)StorageSize[(format)>>4])
146 #define NUMELEM(bytelength, format) ((bytelength) / StorageSize[(format)>>4])
147 #define NUMELEM2(bytelength, format) ((unsigned long)(bytelength) / (unsigned long) StorageSize[(format)>>4])
148 
149 /* Xlib and Xt are permitted to have different memory allocators, and in the
150  * XtSelectionCallbackProc the client is instructed to free the selection
151  * value with XtFree, so the selection value received from XGetWindowProperty
152  * should be copied to memory allocated through Xt.  But copying is
153  * undesirable since the selection value may be large, and, under normal
154  * library configuration copying is unnecessary.
155  */
156 #ifdef XTTRACEMEMORY
157 #define XT_COPY_SELECTION       1
158 #endif
159 
160 static void
FreePropList(Widget w _X_UNUSED,XtPointer closure,XtPointer callData _X_UNUSED)161 FreePropList(Widget w _X_UNUSED,
162              XtPointer closure,
163              XtPointer callData _X_UNUSED)
164 {
165     PropList sarray = (PropList) closure;
166 
167     LOCK_PROCESS;
168     XDeleteContext(sarray->dpy, DefaultRootWindow(sarray->dpy),
169                    selectPropertyContext);
170     UNLOCK_PROCESS;
171     XtFree((char *) sarray->list);
172     XtFree((char *) closure);
173 }
174 
175 static PropList
GetPropList(Display * dpy)176 GetPropList(Display *dpy)
177 {
178     PropList sarray;
179 
180     LOCK_PROCESS;
181     if (selectPropertyContext == 0)
182         selectPropertyContext = XUniqueContext();
183     if (XFindContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
184                      (XPointer *) &sarray)) {
185         Atom atoms[4];
186 
187         static char *names[] = {
188             "INCR",
189             "MULTIPLE",
190             "TIMESTAMP",
191             "_XT_SELECTION_0"
192         };
193 
194         XtPerDisplay pd = _XtGetPerDisplay(dpy);
195 
196         sarray = (PropList) __XtMalloc((unsigned) sizeof(PropListRec));
197         sarray->dpy = dpy;
198         XInternAtoms(dpy, names, 4, FALSE, atoms);
199         sarray->incr_atom = atoms[0];
200         sarray->indirect_atom = atoms[1];
201         sarray->timestamp_atom = atoms[2];
202         sarray->propCount = 1;
203         sarray->list =
204             (SelectionProp) __XtMalloc((unsigned) sizeof(SelectionPropRec));
205         sarray->list[0].prop = atoms[3];
206         sarray->list[0].avail = TRUE;
207         (void) XSaveContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
208                             (char *) sarray);
209         _XtAddCallback(&pd->destroy_callbacks,
210                        FreePropList, (XtPointer) sarray);
211     }
212     UNLOCK_PROCESS;
213     return sarray;
214 }
215 
216 static Atom
GetSelectionProperty(Display * dpy)217 GetSelectionProperty(Display *dpy)
218 {
219     SelectionProp p;
220     int propCount;
221     char propname[80];
222     PropList sarray = GetPropList(dpy);
223 
224     for (p = sarray->list, propCount = sarray->propCount;
225          propCount; p++, propCount--) {
226         if (p->avail) {
227             p->avail = FALSE;
228             return (p->prop);
229         }
230     }
231     propCount = sarray->propCount++;
232     sarray->list = (SelectionProp) XtRealloc((XtPointer) sarray->list,
233                                              (Cardinal) ((size_t) sarray->
234                                                          propCount *
235                                                          sizeof
236                                                          (SelectionPropRec)));
237     (void) snprintf(propname, sizeof(propname), "_XT_SELECTION_%d", propCount);
238     sarray->list[propCount].prop = XInternAtom(dpy, propname, FALSE);
239     sarray->list[propCount].avail = FALSE;
240     return (sarray->list[propCount].prop);
241 }
242 
243 static void
FreeSelectionProperty(Display * dpy,Atom prop)244 FreeSelectionProperty(Display *dpy, Atom prop)
245 {
246     SelectionProp p;
247     int propCount;
248     PropList sarray;
249 
250     if (prop == None)
251         return;
252     LOCK_PROCESS;
253     if (XFindContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
254                      (XPointer *) &sarray))
255         XtAppErrorMsg(XtDisplayToApplicationContext(dpy),
256                       "noSelectionProperties", "freeSelectionProperty",
257                       XtCXtToolkitError,
258                       "internal error: no selection property context for display",
259                       NULL, NULL);
260     UNLOCK_PROCESS;
261     for (p = sarray->list, propCount = sarray->propCount;
262          propCount; p++, propCount--)
263         if (p->prop == prop) {
264             p->avail = TRUE;
265             return;
266         }
267 }
268 
269 static void
FreeInfo(CallBackInfo info)270 FreeInfo(CallBackInfo info)
271 {
272     XtFree((char *) info->incremental);
273     XtFree((char *) info->callbacks);
274     XtFree((char *) info->req_closure);
275     XtFree((char *) info->target);
276     XtFree((char *) info);
277 }
278 
279 static CallBackInfo
MakeInfo(Select ctx,XtSelectionCallbackProc * callbacks,XtPointer * closures,int count,Widget widget,Time time,Boolean * incremental,Atom * properties)280 MakeInfo(Select ctx,
281          XtSelectionCallbackProc *callbacks,
282          XtPointer *closures,
283          int count,
284          Widget widget,
285          Time time,
286          Boolean *incremental,
287          Atom *properties)
288 {
289     CallBackInfo info = XtNew(CallBackInfoRec);
290 
291     info->ctx = ctx;
292     info->callbacks = (XtSelectionCallbackProc *)
293         __XtMalloc((unsigned)
294                    ((size_t) count * sizeof(XtSelectionCallbackProc)));
295     (void) memmove((char *) info->callbacks, (char *) callbacks,
296                    (size_t) count * sizeof(XtSelectionCallbackProc));
297     info->req_closure = (XtPointer *)
298         __XtMalloc((unsigned) ((size_t) count * sizeof(XtPointer)));
299     (void) memmove((char *) info->req_closure, (char *) closures,
300                    (size_t) count * sizeof(XtPointer));
301     if (count == 1 && properties != NULL && properties[0] != None)
302         info->property = properties[0];
303     else {
304         info->property = GetSelectionProperty(XtDisplay(widget));
305         XDeleteProperty(XtDisplay(widget), XtWindow(widget), info->property);
306     }
307     info->proc = HandleSelectionReplies;
308     info->widget = widget;
309     info->time = time;
310     info->incremental =
311         (Boolean *) __XtMalloc((Cardinal) ((size_t) count * sizeof(Boolean)));
312     (void) memmove((char *) info->incremental, (char *) incremental,
313                    (size_t) count * sizeof(Boolean));
314     info->current = 0;
315     info->value = NULL;
316     return (info);
317 }
318 
319 static void
RequestSelectionValue(CallBackInfo info,Atom selection,Atom target)320 RequestSelectionValue(CallBackInfo info, Atom selection, Atom target)
321 {
322 #ifndef DEBUG_WO_TIMERS
323     XtAppContext app = XtWidgetToApplicationContext(info->widget);
324 
325     info->timeout = XtAppAddTimeOut(app,
326                                     app->selectionTimeout, ReqTimedOut,
327                                     (XtPointer) info);
328 #endif
329     XtAddEventHandler(info->widget, (EventMask) 0, TRUE,
330                       HandleSelectionReplies, (XtPointer) info);
331     XConvertSelection(info->ctx->dpy, selection, target,
332                       info->property, XtWindow(info->widget), info->time);
333 }
334 
335 static XContext selectContext = 0;
336 
337 static Select
NewContext(Display * dpy,Atom selection)338 NewContext(Display *dpy, Atom selection)
339 {
340     /* assert(selectContext != 0) */
341     Select ctx = XtNew(SelectRec);
342 
343     ctx->dpy = dpy;
344     ctx->selection = selection;
345     ctx->widget = NULL;
346     ctx->prop_list = GetPropList(dpy);
347     ctx->ref_count = 0;
348     ctx->free_when_done = FALSE;
349     ctx->was_disowned = FALSE;
350     LOCK_PROCESS;
351     (void) XSaveContext(dpy, (Window) selection, selectContext, (char *) ctx);
352     UNLOCK_PROCESS;
353     return ctx;
354 }
355 
356 static Select
FindCtx(Display * dpy,Atom selection)357 FindCtx(Display *dpy, Atom selection)
358 {
359     Select ctx;
360 
361     LOCK_PROCESS;
362     if (selectContext == 0)
363         selectContext = XUniqueContext();
364     if (XFindContext(dpy, (Window) selection, selectContext, (XPointer *) &ctx))
365         ctx = NewContext(dpy, selection);
366     UNLOCK_PROCESS;
367     return ctx;
368 }
369 
370 static void
WidgetDestroyed(Widget widget,XtPointer closure,XtPointer data _X_UNUSED)371 WidgetDestroyed(Widget widget, XtPointer closure, XtPointer data _X_UNUSED)
372 {
373     Select ctx = (Select) closure;
374 
375     if (ctx->widget == widget) {
376         if (ctx->free_when_done)
377             XtFree((char *) ctx);
378         else
379             ctx->widget = NULL;
380     }
381 }
382 
383 /* Selection Owner code */
384 
385 static void HandleSelectionEvents(Widget, XtPointer, XEvent *, Boolean *);
386 
387 static Boolean
LoseSelection(Select ctx,Widget widget,Atom selection,Time time)388 LoseSelection(Select ctx, Widget widget, Atom selection, Time time)
389 {
390     if ((ctx->widget == widget) && (ctx->selection == selection) &&     /* paranoia */
391         !ctx->was_disowned && ((time == CurrentTime) || (time >= ctx->time))) {
392         XtRemoveEventHandler(widget, (EventMask) 0, TRUE,
393                              HandleSelectionEvents, (XtPointer) ctx);
394         XtRemoveCallback(widget, XtNdestroyCallback,
395                          WidgetDestroyed, (XtPointer) ctx);
396         ctx->was_disowned = TRUE;       /* widget officially loses ownership */
397         /* now inform widget */
398         if (ctx->loses) {
399             if (ctx->incremental)
400                 (*(XtLoseSelectionIncrProc) ctx->loses)
401                     (widget, &ctx->selection, ctx->owner_closure);
402             else
403                 (*ctx->loses) (widget, &ctx->selection);
404         }
405         return (TRUE);
406     }
407     else
408         return (FALSE);
409 }
410 
411 static XContext selectWindowContext = 0;
412 
413 /* %%% Xlib.h should make this public! */
414 typedef int (*xErrorHandler) (Display *, XErrorEvent *);
415 
416 static xErrorHandler oldErrorHandler = NULL;
417 static unsigned long firstProtectRequest;
418 static Window errorWindow;
419 
420 static int
LocalErrorHandler(Display * dpy,XErrorEvent * error)421 LocalErrorHandler(Display *dpy, XErrorEvent *error)
422 {
423     int retval;
424 
425     /* If BadWindow error on selection requestor, nothing to do but let
426      * the transfer timeout.  Otherwise, invoke saved error handler. */
427 
428     LOCK_PROCESS;
429 
430     if (error->error_code == BadWindow && error->resourceid == errorWindow &&
431         error->serial >= firstProtectRequest) {
432         UNLOCK_PROCESS;
433         return 0;
434     }
435 
436     if (oldErrorHandler == NULL) {
437         UNLOCK_PROCESS;
438         return 0;               /* should never happen */
439     }
440 
441     retval = (*oldErrorHandler) (dpy, error);
442     UNLOCK_PROCESS;
443     return retval;
444 }
445 
446 static void
StartProtectedSection(Display * dpy,Window window)447 StartProtectedSection(Display *dpy, Window window)
448 {
449     /* protect ourselves against request window being destroyed
450      * before completion of transfer */
451 
452     LOCK_PROCESS;
453     oldErrorHandler = XSetErrorHandler(LocalErrorHandler);
454     firstProtectRequest = NextRequest(dpy);
455     errorWindow = window;
456     UNLOCK_PROCESS;
457 }
458 
459 static void
EndProtectedSection(Display * dpy)460 EndProtectedSection(Display *dpy)
461 {
462     /* flush any generated errors on requestor and
463      * restore original error handler */
464 
465     XSync(dpy, False);
466 
467     LOCK_PROCESS;
468     XSetErrorHandler(oldErrorHandler);
469     oldErrorHandler = NULL;
470     UNLOCK_PROCESS;
471 }
472 
473 static void
AddHandler(Request req,EventMask mask,XtEventHandler proc,XtPointer closure)474 AddHandler(Request req, EventMask mask, XtEventHandler proc, XtPointer closure)
475 {
476     Display *dpy = req->ctx->dpy;
477     Window window = req->requestor;
478     Widget widget = XtWindowToWidget(dpy, window);
479 
480     if (widget != NULL)
481         req->widget = widget;
482     else
483         widget = req->widget;
484 
485     if (XtWindow(widget) == window)
486         XtAddEventHandler(widget, mask, False, proc, closure);
487     else {
488         RequestWindowRec *requestWindowRec;
489 
490         LOCK_PROCESS;
491         if (selectWindowContext == 0)
492             selectWindowContext = XUniqueContext();
493         if (XFindContext(dpy, window, selectWindowContext,
494                          (XPointer *) &requestWindowRec)) {
495             requestWindowRec = XtNew(RequestWindowRec);
496             requestWindowRec->active_transfer_count = 0;
497             (void) XSaveContext(dpy, window, selectWindowContext,
498                                 (char *) requestWindowRec);
499         }
500         UNLOCK_PROCESS;
501         if (requestWindowRec->active_transfer_count++ == 0) {
502             XtRegisterDrawable(dpy, window, widget);
503             XSelectInput(dpy, window, (long) mask);
504         }
505         XtAddRawEventHandler(widget, mask, FALSE, proc, closure);
506     }
507 }
508 
509 static void
RemoveHandler(Request req,EventMask mask,XtEventHandler proc,XtPointer closure)510 RemoveHandler(Request req,
511               EventMask mask,
512               XtEventHandler proc,
513               XtPointer closure)
514 {
515     Display *dpy = req->ctx->dpy;
516     Window window = req->requestor;
517     Widget widget = req->widget;
518 
519     if ((XtWindowToWidget(dpy, window) == widget) &&
520         (XtWindow(widget) != window)) {
521         /* we had to hang this window onto our widget; take it off */
522         RequestWindowRec *requestWindowRec;
523 
524         XtRemoveRawEventHandler(widget, mask, TRUE, proc, closure);
525         LOCK_PROCESS;
526         (void) XFindContext(dpy, window, selectWindowContext,
527                             (XPointer *) &requestWindowRec);
528         UNLOCK_PROCESS;
529         if (--requestWindowRec->active_transfer_count == 0) {
530             XtUnregisterDrawable(dpy, window);
531             StartProtectedSection(dpy, window);
532             XSelectInput(dpy, window, 0L);
533             EndProtectedSection(dpy);
534             LOCK_PROCESS;
535             (void) XDeleteContext(dpy, window, selectWindowContext);
536             UNLOCK_PROCESS;
537             XtFree((char *) requestWindowRec);
538         }
539     }
540     else {
541         XtRemoveEventHandler(widget, mask, TRUE, proc, closure);
542     }
543 }
544 
545 static void
OwnerTimedOut(XtPointer closure,XtIntervalId * id _X_UNUSED)546 OwnerTimedOut(XtPointer closure, XtIntervalId *id _X_UNUSED)
547 {
548     Request req = (Request) closure;
549     Select ctx = req->ctx;
550 
551     if (ctx->incremental && (ctx->owner_cancel != NULL)) {
552         (*ctx->owner_cancel) (ctx->widget, &ctx->selection,
553                               &req->target, (XtRequestId *) &req,
554                               ctx->owner_closure);
555     }
556     else {
557         if (ctx->notify == NULL)
558             XtFree((char *) req->value);
559         else {
560             /* the requestor hasn't deleted the property, but
561              * the owner needs to free the value.
562              */
563             if (ctx->incremental)
564                 (*(XtSelectionDoneIncrProc) ctx->notify)
565                     (ctx->widget, &ctx->selection, &req->target,
566                      (XtRequestId *) &req, ctx->owner_closure);
567             else
568                 (*ctx->notify) (ctx->widget, &ctx->selection, &req->target);
569         }
570     }
571 
572     RemoveHandler(req, (EventMask) PropertyChangeMask,
573                   HandlePropertyGone, closure);
574     XtFree((char *) req);
575     if (--ctx->ref_count == 0 && ctx->free_when_done)
576         XtFree((char *) ctx);
577 }
578 
579 static void
SendIncrement(Request incr)580 SendIncrement(Request incr)
581 {
582     Display *dpy = incr->ctx->dpy;
583 
584     unsigned long incrSize = (unsigned long) MAX_SELECTION_INCR(dpy);
585 
586     if (incrSize > incr->bytelength - incr->offset)
587         incrSize = incr->bytelength - incr->offset;
588     StartProtectedSection(dpy, incr->requestor);
589     XChangeProperty(dpy, incr->requestor, incr->property,
590                     incr->type, incr->format, PropModeReplace,
591                     (unsigned char *) incr->value + incr->offset,
592                     NUMELEM((int) incrSize, incr->format));
593     EndProtectedSection(dpy);
594     incr->offset += incrSize;
595 }
596 
597 static void
AllSent(Request req)598 AllSent(Request req)
599 {
600     Select ctx = req->ctx;
601 
602     StartProtectedSection(ctx->dpy, req->requestor);
603     XChangeProperty(ctx->dpy, req->requestor,
604                     req->property, req->type, req->format,
605                     PropModeReplace, (unsigned char *) NULL, 0);
606     EndProtectedSection(ctx->dpy);
607     req->allSent = TRUE;
608 
609     if (ctx->notify == NULL)
610         XtFree((char *) req->value);
611 }
612 
613 static void
HandlePropertyGone(Widget widget _X_UNUSED,XtPointer closure,XEvent * ev,Boolean * cont _X_UNUSED)614 HandlePropertyGone(Widget widget _X_UNUSED,
615                    XtPointer closure,
616                    XEvent *ev,
617                    Boolean *cont _X_UNUSED)
618 {
619     XPropertyEvent *event = (XPropertyEvent *) ev;
620     Request req = (Request) closure;
621     Select ctx = req->ctx;
622 
623     if ((event->type != PropertyNotify) ||
624         (event->state != PropertyDelete) ||
625         (event->atom != req->property) || (event->window != req->requestor))
626         return;
627 #ifndef DEBUG_WO_TIMERS
628     XtRemoveTimeOut(req->timeout);
629 #endif
630     if (req->allSent) {
631         if (ctx->notify) {
632             if (ctx->incremental) {
633                 (*(XtSelectionDoneIncrProc) ctx->notify)
634                     (ctx->widget, &ctx->selection, &req->target,
635                      (XtRequestId *) &req, ctx->owner_closure);
636             }
637             else
638                 (*ctx->notify) (ctx->widget, &ctx->selection, &req->target);
639         }
640         RemoveHandler(req, (EventMask) PropertyChangeMask,
641                       HandlePropertyGone, closure);
642         XtFree((char *) req);
643         if (--ctx->ref_count == 0 && ctx->free_when_done)
644             XtFree((char *) ctx);
645     }
646     else {                      /* is this part of an incremental transfer? */
647         if (ctx->incremental) {
648             if (req->bytelength == 0)
649                 AllSent(req);
650             else {
651                 unsigned long size =
652                     (unsigned long) MAX_SELECTION_INCR(ctx->dpy);
653                 SendIncrement(req);
654                 (*(XtConvertSelectionIncrProc) ctx->convert)
655                     (ctx->widget, &ctx->selection, &req->target,
656                      &req->type, &req->value,
657                      &req->bytelength, &req->format,
658                      &size, ctx->owner_closure, (XtPointer *) &req);
659                 if (req->bytelength)
660                     req->bytelength = BYTELENGTH(req->bytelength, req->format);
661                 req->offset = 0;
662             }
663         }
664         else {
665             if (req->offset < req->bytelength)
666                 SendIncrement(req);
667             else
668                 AllSent(req);
669         }
670 #ifndef DEBUG_WO_TIMERS
671         {
672             XtAppContext app = XtWidgetToApplicationContext(req->widget);
673 
674             req->timeout = XtAppAddTimeOut(app,
675                                            app->selectionTimeout, OwnerTimedOut,
676                                            (XtPointer) req);
677         }
678 #endif
679     }
680 }
681 
682 static void
PrepareIncremental(Request req,Widget widget,Window window,Atom property _X_UNUSED,Atom target,Atom targetType,XtPointer value,unsigned long length,int format)683 PrepareIncremental(Request req,
684                    Widget widget,
685                    Window window,
686                    Atom property _X_UNUSED,
687                    Atom target,
688                    Atom targetType,
689                    XtPointer value,
690                    unsigned long length,
691                    int format)
692 {
693     req->type = targetType;
694     req->value = value;
695     req->bytelength = BYTELENGTH(length, format);
696     req->format = format;
697     req->offset = 0;
698     req->target = target;
699     req->widget = widget;
700     req->allSent = FALSE;
701 #ifndef DEBUG_WO_TIMERS
702     {
703         XtAppContext app = XtWidgetToApplicationContext(widget);
704 
705         req->timeout = XtAppAddTimeOut(app,
706                                        app->selectionTimeout, OwnerTimedOut,
707                                        (XtPointer) req);
708     }
709 #endif
710     AddHandler(req, (EventMask) PropertyChangeMask,
711                HandlePropertyGone, (XtPointer) req);
712 /* now send client INCR property */
713     XChangeProperty(req->ctx->dpy, window, req->property,
714                     req->ctx->prop_list->incr_atom,
715                     32, PropModeReplace, (unsigned char *) &req->bytelength, 1);
716 }
717 
718 static Boolean
GetConversion(Select ctx,XSelectionRequestEvent * event,Atom target,Atom property,Widget widget)719 GetConversion(Select ctx,       /* logical owner */
720               XSelectionRequestEvent *event,
721               Atom target,
722               Atom property,    /* requestor's property */
723               Widget widget)    /* physical owner (receives events) */
724 {
725     XtPointer value = NULL;
726     unsigned long length;
727     int format;
728     Atom targetType;
729     Request req = XtNew(RequestRec);
730     Boolean timestamp_target = (target == ctx->prop_list->timestamp_atom);
731 
732     req->ctx = ctx;
733     req->event = *event;
734     req->property = property;
735     req->requestor = event->requestor;
736 
737     if (timestamp_target) {
738         value = __XtMalloc(sizeof(long));
739         *(long *) value = (long) ctx->time;
740         targetType = XA_INTEGER;
741         length = 1;
742         format = 32;
743     }
744     else {
745         ctx->ref_count++;
746         if (ctx->incremental == TRUE) {
747             unsigned long size = (unsigned long) MAX_SELECTION_INCR(ctx->dpy);
748 
749             if ((*(XtConvertSelectionIncrProc) ctx->convert)
750                 (ctx->widget, &event->selection, &target,
751                  &targetType, &value, &length, &format,
752                  &size, ctx->owner_closure, (XtRequestId *) &req)
753                 == FALSE) {
754                 XtFree((char *) req);
755                 ctx->ref_count--;
756                 return (FALSE);
757             }
758             StartProtectedSection(ctx->dpy, event->requestor);
759             PrepareIncremental(req, widget, event->requestor, property,
760                                target, targetType, value, length, format);
761             return (TRUE);
762         }
763         ctx->req = req;
764         if ((*ctx->convert) (ctx->widget, &event->selection, &target,
765                              &targetType, &value, &length, &format) == FALSE) {
766             XtFree((char *) req);
767             ctx->req = NULL;
768             ctx->ref_count--;
769             return (FALSE);
770         }
771         ctx->req = NULL;
772     }
773     StartProtectedSection(ctx->dpy, event->requestor);
774     if (BYTELENGTH(length, format) <=
775         (unsigned long) MAX_SELECTION_INCR(ctx->dpy)) {
776         if (!timestamp_target) {
777             if (ctx->notify != NULL) {
778                 req->target = target;
779                 req->widget = widget;
780                 req->allSent = TRUE;
781 #ifndef DEBUG_WO_TIMERS
782                 {
783                     XtAppContext app =
784                         XtWidgetToApplicationContext(req->widget);
785                     req->timeout =
786                         XtAppAddTimeOut(app, app->selectionTimeout,
787                                         OwnerTimedOut, (XtPointer) req);
788                 }
789 #endif
790                 AddHandler(req, (EventMask) PropertyChangeMask,
791                            HandlePropertyGone, (XtPointer) req);
792             }
793             else
794                 ctx->ref_count--;
795         }
796         XChangeProperty(ctx->dpy, event->requestor, property,
797                         targetType, format, PropModeReplace,
798                         (unsigned char *) value, (int) length);
799         /* free storage for client if no notify proc */
800         if (timestamp_target || ctx->notify == NULL) {
801             XtFree((char *) value);
802             XtFree((char *) req);
803         }
804     }
805     else {
806         PrepareIncremental(req, widget, event->requestor, property,
807                            target, targetType, value, length, format);
808     }
809     return (TRUE);
810 }
811 
812 static void
HandleSelectionEvents(Widget widget,XtPointer closure,XEvent * event,Boolean * cont _X_UNUSED)813 HandleSelectionEvents(Widget widget,
814                       XtPointer closure,
815                       XEvent *event,
816                       Boolean *cont _X_UNUSED)
817 {
818     Select ctx;
819     XSelectionEvent ev;
820     Atom target;
821 
822     ctx = (Select) closure;
823     switch (event->type) {
824     case SelectionClear:
825         /* if this event is not for the selection we registered for,
826          * don't do anything */
827         if (ctx->selection != event->xselectionclear.selection ||
828             ctx->serial > event->xselectionclear.serial)
829             break;
830         (void) LoseSelection(ctx, widget, event->xselectionclear.selection,
831                              event->xselectionclear.time);
832         break;
833     case SelectionRequest:
834         /* if this event is not for the selection we registered for,
835          * don't do anything */
836         if (ctx->selection != event->xselectionrequest.selection)
837             break;
838         ev.type = SelectionNotify;
839         ev.display = event->xselectionrequest.display;
840 
841         ev.requestor = event->xselectionrequest.requestor;
842         ev.selection = event->xselectionrequest.selection;
843         ev.time = event->xselectionrequest.time;
844         ev.target = event->xselectionrequest.target;
845         if (event->xselectionrequest.property == None)  /* obsolete requestor */
846             event->xselectionrequest.property = event->xselectionrequest.target;
847         if (ctx->widget != widget || ctx->was_disowned
848             || ((event->xselectionrequest.time != CurrentTime)
849                 && (event->xselectionrequest.time < ctx->time))) {
850             ev.property = None;
851             StartProtectedSection(ev.display, ev.requestor);
852         }
853         else {
854             if (ev.target == ctx->prop_list->indirect_atom) {
855                 IndirectPair *p;
856                 int format;
857                 unsigned long bytesafter, length;
858                 unsigned char *value = NULL;
859                 int count;
860                 Boolean writeback = FALSE;
861 
862                 ev.property = event->xselectionrequest.property;
863                 StartProtectedSection(ev.display, ev.requestor);
864                 if (XGetWindowProperty(ev.display, ev.requestor,
865                                        event->xselectionrequest.property, 0L,
866                                        1000000, False, (Atom) AnyPropertyType,
867                                        &target, &format, &length, &bytesafter,
868                                        &value) == Success)
869                     count =
870                         (int) (BYTELENGTH(length, format) /
871                                sizeof(IndirectPair));
872                 else
873                     count = 0;
874                 for (p = (IndirectPair *) value; count; p++, count--) {
875                     EndProtectedSection(ctx->dpy);
876                     if (!GetConversion(ctx, (XSelectionRequestEvent *) event,
877                                        p->target, p->property, widget)) {
878 
879                         p->target = None;
880                         writeback = TRUE;
881                         StartProtectedSection(ctx->dpy, ev.requestor);
882                     }
883                 }
884                 if (writeback)
885                     XChangeProperty(ev.display, ev.requestor,
886                                     event->xselectionrequest.property, target,
887                                     format, PropModeReplace, value,
888                                     (int) length);
889                 XFree((char *) value);
890             }
891             else {              /* not multiple */
892 
893                 if (GetConversion(ctx, (XSelectionRequestEvent *) event,
894                                   event->xselectionrequest.target,
895                                   event->xselectionrequest.property, widget))
896                     ev.property = event->xselectionrequest.property;
897                 else {
898                     ev.property = None;
899                     StartProtectedSection(ctx->dpy, ev.requestor);
900                 }
901             }
902         }
903         (void) XSendEvent(ctx->dpy, ev.requestor, False, (unsigned long) NULL,
904                           (XEvent *) &ev);
905 
906         EndProtectedSection(ctx->dpy);
907 
908         break;
909     }
910 }
911 
912 static Boolean
OwnSelection(Widget widget,Atom selection,Time time,XtConvertSelectionProc convert,XtLoseSelectionProc lose,XtSelectionDoneProc notify,XtCancelConvertSelectionProc cancel,XtPointer closure,Boolean incremental)913 OwnSelection(Widget widget,
914              Atom selection,
915              Time time,
916              XtConvertSelectionProc convert,
917              XtLoseSelectionProc lose,
918              XtSelectionDoneProc notify,
919              XtCancelConvertSelectionProc cancel,
920              XtPointer closure,
921              Boolean incremental)
922 {
923     Select ctx;
924     Select oldctx = NULL;
925 
926     if (!XtIsRealized(widget))
927         return False;
928 
929     ctx = FindCtx(XtDisplay(widget), selection);
930     if (ctx->widget != widget || ctx->time != time ||
931         ctx->ref_count || ctx->was_disowned) {
932         Boolean replacement = FALSE;
933         Window window = XtWindow(widget);
934         unsigned long serial = XNextRequest(ctx->dpy);
935 
936         XSetSelectionOwner(ctx->dpy, selection, window, time);
937         if (XGetSelectionOwner(ctx->dpy, selection) != window)
938             return FALSE;
939         if (ctx->ref_count) {   /* exchange is in-progress */
940 #ifdef DEBUG_ACTIVE
941             printf
942                 ("Active exchange for widget \"%s\"; selection=0x%lx, ref_count=%d\n",
943                  XtName(widget), (long) selection, ctx->ref_count);
944 #endif
945             if (ctx->widget != widget ||
946                 ctx->convert != convert ||
947                 ctx->loses != lose ||
948                 ctx->notify != notify ||
949                 ctx->owner_cancel != cancel ||
950                 ctx->incremental != incremental ||
951                 ctx->owner_closure != closure) {
952                 if (ctx->widget == widget) {
953                     XtRemoveEventHandler(widget, (EventMask) 0, TRUE,
954                                          HandleSelectionEvents,
955                                          (XtPointer) ctx);
956                     XtRemoveCallback(widget, XtNdestroyCallback,
957                                      WidgetDestroyed, (XtPointer) ctx);
958                     replacement = TRUE;
959                 }
960                 else if (!ctx->was_disowned) {
961                     oldctx = ctx;
962                 }
963                 ctx->free_when_done = TRUE;
964                 ctx = NewContext(XtDisplay(widget), selection);
965             }
966             else if (!ctx->was_disowned) {      /* current owner is new owner */
967                 ctx->time = time;
968                 return TRUE;
969             }
970         }
971         if (ctx->widget != widget || ctx->was_disowned || replacement) {
972             if (ctx->widget && !ctx->was_disowned && !replacement) {
973                 oldctx = ctx;
974                 oldctx->free_when_done = TRUE;
975                 ctx = NewContext(XtDisplay(widget), selection);
976             }
977             XtAddEventHandler(widget, (EventMask) 0, TRUE,
978                               HandleSelectionEvents, (XtPointer) ctx);
979             XtAddCallback(widget, XtNdestroyCallback,
980                           WidgetDestroyed, (XtPointer) ctx);
981         }
982         ctx->widget = widget;   /* Selection offically changes hands. */
983         ctx->time = time;
984         ctx->serial = serial;
985     }
986     ctx->convert = convert;
987     ctx->loses = lose;
988     ctx->notify = notify;
989     ctx->owner_cancel = cancel;
990     XtSetBit(ctx->incremental, incremental);
991     ctx->owner_closure = closure;
992     ctx->was_disowned = FALSE;
993 
994     /* Defer calling the previous selection owner's lose selection procedure
995      * until the new selection is established, to allow the previous
996      * selection owner to ask for the new selection to be converted in
997      * the lose selection procedure.  The context pointer is the closure
998      * of the event handler and the destroy callback, so the old context
999      * pointer and the record contents must be preserved for LoseSelection.
1000      */
1001     if (oldctx) {
1002         (void) LoseSelection(oldctx, oldctx->widget, selection, oldctx->time);
1003         if (!oldctx->ref_count && oldctx->free_when_done)
1004             XtFree((char *) oldctx);
1005     }
1006     return TRUE;
1007 }
1008 
1009 Boolean
XtOwnSelection(Widget widget,Atom selection,Time time,XtConvertSelectionProc convert,XtLoseSelectionProc lose,XtSelectionDoneProc notify)1010 XtOwnSelection(Widget widget,
1011                Atom selection,
1012                Time time,
1013                XtConvertSelectionProc convert,
1014                XtLoseSelectionProc lose,
1015                XtSelectionDoneProc notify)
1016 {
1017     Boolean retval;
1018 
1019     WIDGET_TO_APPCON(widget);
1020 
1021     LOCK_APP(app);
1022     retval = OwnSelection(widget, selection, time, convert, lose, notify,
1023                           (XtCancelConvertSelectionProc) NULL,
1024                           (XtPointer) NULL, FALSE);
1025     UNLOCK_APP(app);
1026     return retval;
1027 }
1028 
1029 Boolean
XtOwnSelectionIncremental(Widget widget,Atom selection,Time time,XtConvertSelectionIncrProc convert,XtLoseSelectionIncrProc lose,XtSelectionDoneIncrProc notify,XtCancelConvertSelectionProc cancel,XtPointer closure)1030 XtOwnSelectionIncremental(Widget widget,
1031                           Atom selection,
1032                           Time time,
1033                           XtConvertSelectionIncrProc convert,
1034                           XtLoseSelectionIncrProc lose,
1035                           XtSelectionDoneIncrProc notify,
1036                           XtCancelConvertSelectionProc cancel,
1037                           XtPointer closure)
1038 {
1039     Boolean retval;
1040 
1041     WIDGET_TO_APPCON(widget);
1042 
1043     LOCK_APP(app);
1044     retval = OwnSelection(widget, selection, time,
1045                           (XtConvertSelectionProc) convert,
1046                           (XtLoseSelectionProc) lose,
1047                           (XtSelectionDoneProc) notify, cancel, closure, TRUE);
1048     UNLOCK_APP(app);
1049     return retval;
1050 }
1051 
1052 void
XtDisownSelection(Widget widget,Atom selection,Time time)1053 XtDisownSelection(Widget widget, Atom selection, Time time)
1054 {
1055     Select ctx;
1056 
1057     WIDGET_TO_APPCON(widget);
1058 
1059     LOCK_APP(app);
1060     ctx = FindCtx(XtDisplay(widget), selection);
1061     if (LoseSelection(ctx, widget, selection, time))
1062         XSetSelectionOwner(XtDisplay(widget), selection, None, time);
1063     UNLOCK_APP(app);
1064 }
1065 
1066 /* Selection Requestor code */
1067 
1068 static Boolean
IsINCRtype(CallBackInfo info,Window window,Atom prop)1069 IsINCRtype(CallBackInfo info, Window window, Atom prop)
1070 {
1071     unsigned long bytesafter;
1072     unsigned long length;
1073     int format;
1074     Atom type;
1075     unsigned char *value;
1076 
1077     if (prop == None)
1078         return False;
1079 
1080     if (XGetWindowProperty(XtDisplay(info->widget), window, prop, 0L, 0L,
1081                            False, info->ctx->prop_list->incr_atom, &type,
1082                            &format, &length, &bytesafter, &value) != Success)
1083         return False;
1084 
1085     return (type == info->ctx->prop_list->incr_atom);
1086 }
1087 
1088 static void
ReqCleanup(Widget widget,XtPointer closure,XEvent * ev,Boolean * cont _X_UNUSED)1089 ReqCleanup(Widget widget,
1090            XtPointer closure,
1091            XEvent *ev,
1092            Boolean *cont _X_UNUSED)
1093 {
1094     CallBackInfo info = (CallBackInfo) closure;
1095     unsigned long bytesafter, length;
1096     int format;
1097     Atom target;
1098 
1099     if (ev->type == SelectionNotify) {
1100         XSelectionEvent *event = (XSelectionEvent *) ev;
1101 
1102         if (!MATCH_SELECT(event, info))
1103             return;             /* not really for us */
1104         XtRemoveEventHandler(widget, (EventMask) 0, TRUE,
1105                              ReqCleanup, (XtPointer) info);
1106         if (IsINCRtype(info, XtWindow(widget), event->property)) {
1107             info->proc = HandleGetIncrement;
1108             XtAddEventHandler(info->widget, (EventMask) PropertyChangeMask,
1109                               FALSE, ReqCleanup, (XtPointer) info);
1110         }
1111         else {
1112             if (event->property != None)
1113                 XDeleteProperty(event->display, XtWindow(widget),
1114                                 event->property);
1115             FreeSelectionProperty(XtDisplay(widget), info->property);
1116             FreeInfo(info);
1117         }
1118     }
1119     else if ((ev->type == PropertyNotify) &&
1120              (ev->xproperty.state == PropertyNewValue) &&
1121              (ev->xproperty.atom == info->property)) {
1122         XPropertyEvent *event = (XPropertyEvent *) ev;
1123         char *value = NULL;
1124 
1125         if (XGetWindowProperty(event->display, XtWindow(widget),
1126                                event->atom, 0L, 1000000, True, AnyPropertyType,
1127                                &target, &format, &length, &bytesafter,
1128                                (unsigned char **) &value) == Success) {
1129             XFree(value);
1130             if (length == 0) {
1131                 XtRemoveEventHandler(widget, (EventMask) PropertyChangeMask,
1132                                      FALSE, ReqCleanup, (XtPointer) info);
1133                 FreeSelectionProperty(XtDisplay(widget), info->property);
1134                 XtFree(info->value);    /* requestor never got this, so free now */
1135                 FreeInfo(info);
1136             }
1137         }
1138     }
1139 }
1140 
1141 static void
ReqTimedOut(XtPointer closure,XtIntervalId * id _X_UNUSED)1142 ReqTimedOut(XtPointer closure, XtIntervalId *id _X_UNUSED)
1143 {
1144     XtPointer value = NULL;
1145     unsigned long length = 0;
1146     int format = 8;
1147     Atom resulttype = XT_CONVERT_FAIL;
1148     CallBackInfo info = (CallBackInfo) closure;
1149     unsigned long bytesafter;
1150     unsigned long proplength;
1151     Atom type;
1152 
1153     if (*info->target == info->ctx->prop_list->indirect_atom) {
1154         IndirectPair *pairs = NULL;
1155 
1156         if (XGetWindowProperty(XtDisplay(info->widget), XtWindow(info->widget),
1157                                info->property, 0L, 10000000, True,
1158                                AnyPropertyType, &type, &format, &proplength,
1159                                &bytesafter, (unsigned char **) &pairs)
1160             == Success) {
1161             XtPointer *c;
1162             int i;
1163 
1164             XFree(pairs);
1165             for (proplength = proplength / IndirectPairWordSize, i = 0,
1166                  c = info->req_closure; proplength; proplength--, c++, i++)
1167                 (*info->callbacks[i]) (info->widget, *c, &info->ctx->selection,
1168                                        &resulttype, value, &length, &format);
1169         }
1170     }
1171     else {
1172         (*info->callbacks[0]) (info->widget, *info->req_closure,
1173                                &info->ctx->selection, &resulttype, value,
1174                                &length, &format);
1175     }
1176 
1177     /* change event handlers for straggler events */
1178     if (info->proc == (XtEventHandler) HandleSelectionReplies) {
1179         XtRemoveEventHandler(info->widget, (EventMask) 0,
1180                              TRUE, info->proc, (XtPointer) info);
1181         XtAddEventHandler(info->widget, (EventMask) 0, TRUE,
1182                           ReqCleanup, (XtPointer) info);
1183     }
1184     else {
1185         XtRemoveEventHandler(info->widget, (EventMask) PropertyChangeMask,
1186                              FALSE, info->proc, (XtPointer) info);
1187         XtAddEventHandler(info->widget, (EventMask) PropertyChangeMask,
1188                           FALSE, ReqCleanup, (XtPointer) info);
1189     }
1190 
1191 }
1192 
1193 static void
HandleGetIncrement(Widget widget,XtPointer closure,XEvent * ev,Boolean * cont _X_UNUSED)1194 HandleGetIncrement(Widget widget,
1195                    XtPointer closure,
1196                    XEvent *ev,
1197                    Boolean *cont _X_UNUSED)
1198 {
1199     XPropertyEvent *event = (XPropertyEvent *) ev;
1200     CallBackInfo info = (CallBackInfo) closure;
1201     Select ctx = info->ctx;
1202     char *value;
1203     unsigned long bytesafter;
1204     unsigned long length;
1205     int bad;
1206     int n = info->current;
1207 
1208     if ((event->state != PropertyNewValue) || (event->atom != info->property))
1209         return;
1210 
1211     bad = XGetWindowProperty(event->display, XtWindow(widget),
1212                              event->atom, 0L,
1213                              10000000, True, AnyPropertyType, &info->type,
1214                              &info->format, &length, &bytesafter,
1215                              (unsigned char **)&value);
1216     if (bad)
1217         return;
1218 #ifndef DEBUG_WO_TIMERS
1219     XtRemoveTimeOut(info->timeout);
1220 #endif
1221     if (length == 0) {
1222         unsigned long u_offset = NUMELEM2(info->offset, info->format);
1223 
1224         (*info->callbacks[n]) (widget, *info->req_closure, &ctx->selection,
1225                                &info->type,
1226                                (info->offset == 0 ? value : info->value),
1227                                &u_offset, &info->format);
1228         /* assert ((info->offset != 0) == (info->incremental[n]) */
1229         if (info->offset != 0)
1230             XFree(value);
1231         XtRemoveEventHandler(widget, (EventMask) PropertyChangeMask, FALSE,
1232                              HandleGetIncrement, (XtPointer) info);
1233         FreeSelectionProperty(event->display, info->property);
1234 
1235         FreeInfo(info);
1236     }
1237     else {                      /* add increment to collection */
1238         if (info->incremental[n]) {
1239 #ifdef XT_COPY_SELECTION
1240             int size = (int) BYTELENGTH(length, info->format) + 1;
1241             char *tmp = __XtMalloc((Cardinal) size);
1242 
1243             (void) memmove(tmp, value, (size_t) size);
1244             XFree(value);
1245             value = tmp;
1246 #endif
1247             (*info->callbacks[n]) (widget, *info->req_closure, &ctx->selection,
1248                                    &info->type, value, &length, &info->format);
1249         }
1250         else {
1251             int size = (int) BYTELENGTH(length, info->format);
1252 
1253             if (info->offset + size > info->bytelength) {
1254                 /* allocate enough for this and the next increment */
1255                 info->bytelength = info->offset + size * 2;
1256                 info->value = XtRealloc(info->value,
1257                                         (Cardinal) info->bytelength);
1258             }
1259             (void) memmove(&info->value[info->offset], value, (size_t) size);
1260             info->offset += size;
1261             XFree(value);
1262         }
1263         /* reset timer */
1264 #ifndef DEBUG_WO_TIMERS
1265         {
1266             XtAppContext app = XtWidgetToApplicationContext(info->widget);
1267 
1268             info->timeout = XtAppAddTimeOut(app,
1269                                             app->selectionTimeout, ReqTimedOut,
1270                                             (XtPointer) info);
1271         }
1272 #endif
1273     }
1274 }
1275 
1276 static void
HandleNone(Widget widget,XtSelectionCallbackProc callback,XtPointer closure,Atom selection)1277 HandleNone(Widget widget,
1278            XtSelectionCallbackProc callback,
1279            XtPointer closure,
1280            Atom selection)
1281 {
1282     unsigned long length = 0;
1283     int format = 8;
1284     Atom type = None;
1285 
1286     (*callback) (widget, closure, &selection, &type, NULL, &length, &format);
1287 }
1288 
1289 static unsigned long
IncrPropSize(Widget widget,unsigned char * value,int format,unsigned long length)1290 IncrPropSize(Widget widget,
1291              unsigned char *value,
1292              int format,
1293              unsigned long length)
1294 {
1295     if (format == 32) {
1296         unsigned long size;
1297 
1298         size = ((unsigned long *) value)[length - 1];   /* %%% what order for longs? */
1299         return size;
1300     }
1301     else {
1302         XtAppWarningMsg(XtWidgetToApplicationContext(widget),
1303                         "badFormat", "xtGetSelectionValue", XtCXtToolkitError,
1304                         "Selection owner returned type INCR property with format != 32",
1305                         NULL, NULL);
1306         return 0;
1307     }
1308 }
1309 
1310 static
1311     Boolean
HandleNormal(Display * dpy,Widget widget,Atom property,CallBackInfo info,XtPointer closure,Atom selection)1312 HandleNormal(Display *dpy,
1313              Widget widget,
1314              Atom property,
1315              CallBackInfo info,
1316              XtPointer closure,
1317              Atom selection)
1318 {
1319     unsigned long bytesafter;
1320     unsigned long length;
1321     int format;
1322     Atom type;
1323     unsigned char *value = NULL;
1324     int number = info->current;
1325 
1326     if (XGetWindowProperty(dpy, XtWindow(widget), property, 0L, 10000000,
1327                            False, AnyPropertyType, &type, &format, &length,
1328                            &bytesafter, &value) != Success)
1329         return FALSE;
1330 
1331     if (type == info->ctx->prop_list->incr_atom) {
1332         unsigned long size = IncrPropSize(widget, value, format, length);
1333 
1334         XFree((char *) value);
1335         if (info->property != property) {
1336             /* within MULTIPLE */
1337             CallBackInfo ninfo;
1338 
1339             ninfo = MakeInfo(info->ctx, &info->callbacks[number],
1340                              &info->req_closure[number], 1, widget,
1341                              info->time, &info->incremental[number], &property);
1342             ninfo->target = (Atom *) __XtMalloc((unsigned) sizeof(Atom));
1343             *ninfo->target = info->target[number + 1];
1344             info = ninfo;
1345         }
1346         HandleIncremental(dpy, widget, property, info, size);
1347         return FALSE;
1348     }
1349 
1350     XDeleteProperty(dpy, XtWindow(widget), property);
1351 #ifdef XT_COPY_SELECTION
1352     if (value) {                /* it could have been deleted after the SelectionNotify */
1353         int size = (int) BYTELENGTH(length, info->format) + 1;
1354         char *tmp = __XtMalloc((Cardinal) size);
1355 
1356         (void) memmove(tmp, value, (size_t) size);
1357         XFree(value);
1358         value = (unsigned char *) tmp;
1359     }
1360 #endif
1361     (*info->callbacks[number]) (widget, closure, &selection,
1362                                 &type, (XtPointer) value, &length, &format);
1363 
1364     if (info->incremental[number]) {
1365         /* let requestor know the whole thing has been received */
1366         value = (unsigned char *) __XtMalloc((unsigned) 1);
1367         length = 0;
1368         (*info->callbacks[number]) (widget, closure, &selection,
1369                                     &type, (XtPointer) value, &length, &format);
1370     }
1371     return TRUE;
1372 }
1373 
1374 static void
HandleIncremental(Display * dpy,Widget widget,Atom property,CallBackInfo info,unsigned long size)1375 HandleIncremental(Display *dpy,
1376                   Widget widget,
1377                   Atom property,
1378                   CallBackInfo info,
1379                   unsigned long size)
1380 {
1381     XtAddEventHandler(widget, (EventMask) PropertyChangeMask, FALSE,
1382                       HandleGetIncrement, (XtPointer) info);
1383 
1384     /* now start the transfer */
1385     XDeleteProperty(dpy, XtWindow(widget), property);
1386     XFlush(dpy);
1387 
1388     info->bytelength = (int) size;
1389     if (info->incremental[info->current])       /* requestor wants incremental too */
1390         info->value = NULL;     /* so no need for buffer to assemble value */
1391     else
1392         info->value = (char *) __XtMalloc((unsigned) info->bytelength);
1393     info->offset = 0;
1394 
1395     /* reset the timer */
1396     info->proc = HandleGetIncrement;
1397 #ifndef DEBUG_WO_TIMERS
1398     {
1399         XtAppContext app = XtWidgetToApplicationContext(info->widget);
1400 
1401         info->timeout = XtAppAddTimeOut(app,
1402                                         app->selectionTimeout, ReqTimedOut,
1403                                         (XtPointer) info);
1404     }
1405 #endif
1406 }
1407 
1408 static void
HandleSelectionReplies(Widget widget,XtPointer closure,XEvent * ev,Boolean * cont _X_UNUSED)1409 HandleSelectionReplies(Widget widget,
1410                        XtPointer closure,
1411                        XEvent *ev,
1412                        Boolean *cont _X_UNUSED)
1413 {
1414     XSelectionEvent *event = (XSelectionEvent *) ev;
1415     Display *dpy = event->display;
1416     CallBackInfo info = (CallBackInfo) closure;
1417     Select ctx = info->ctx;
1418     unsigned long bytesafter;
1419     unsigned long length;
1420     int format;
1421     Atom type;
1422 
1423     if (event->type != SelectionNotify)
1424         return;
1425     if (!MATCH_SELECT(event, info))
1426         return;                 /* not really for us */
1427 #ifndef DEBUG_WO_TIMERS
1428     XtRemoveTimeOut(info->timeout);
1429 #endif
1430     XtRemoveEventHandler(widget, (EventMask) 0, TRUE,
1431                          HandleSelectionReplies, (XtPointer) info);
1432     if (event->target == ctx->prop_list->indirect_atom) {
1433         IndirectPair *pairs = NULL, *p;
1434         XtPointer *c;
1435 
1436         if (XGetWindowProperty(dpy, XtWindow(widget), info->property, 0L,
1437                                10000000, True, AnyPropertyType, &type, &format,
1438                                &length, &bytesafter, (unsigned char **) &pairs)
1439             != Success)
1440             length = 0;
1441         for (length = length / IndirectPairWordSize, p = pairs,
1442              c = info->req_closure;
1443              length; length--, p++, c++, info->current++) {
1444             if (event->property == None || format != 32 || p->target == None
1445                 || /* bug compatibility */ p->property == None) {
1446                 HandleNone(widget, info->callbacks[info->current],
1447                            *c, event->selection);
1448                 if (p->property != None)
1449                     FreeSelectionProperty(XtDisplay(widget), p->property);
1450             }
1451             else {
1452                 if (HandleNormal(dpy, widget, p->property, info, *c,
1453                                  event->selection)) {
1454                     FreeSelectionProperty(XtDisplay(widget), p->property);
1455                 }
1456             }
1457         }
1458         XFree((char *) pairs);
1459         FreeSelectionProperty(dpy, info->property);
1460         FreeInfo(info);
1461     }
1462     else if (event->property == None) {
1463         HandleNone(widget, info->callbacks[0], *info->req_closure,
1464                    event->selection);
1465         FreeSelectionProperty(XtDisplay(widget), info->property);
1466         FreeInfo(info);
1467     }
1468     else {
1469         if (HandleNormal(dpy, widget, event->property, info,
1470                          *info->req_closure, event->selection)) {
1471             FreeSelectionProperty(XtDisplay(widget), info->property);
1472             FreeInfo(info);
1473         }
1474     }
1475 }
1476 
1477 static void
DoLocalTransfer(Request req,Atom selection,Atom target,Widget widget,XtSelectionCallbackProc callback,XtPointer closure,Boolean incremental,Atom property)1478 DoLocalTransfer(Request req,
1479                 Atom selection,
1480                 Atom target,
1481                 Widget widget, /* The widget requesting the value. */
1482                 XtSelectionCallbackProc callback,
1483                 XtPointer closure,    /* the closure for the callback, not the conversion */
1484                 Boolean incremental, Atom property)
1485 {
1486     Select ctx = req->ctx;
1487     XtPointer value = NULL, temp, total = NULL;
1488     unsigned long length;
1489     int format;
1490     Atom resulttype;
1491     unsigned long totallength = 0;
1492 
1493     req->event.type = 0;
1494     req->event.target = target;
1495     req->event.property = req->property = property;
1496     req->event.requestor = req->requestor = XtWindow(widget);
1497 
1498     if (ctx->incremental) {
1499         unsigned long size = (unsigned long) MAX_SELECTION_INCR(ctx->dpy);
1500 
1501         if (!(*(XtConvertSelectionIncrProc) ctx->convert)
1502             (ctx->widget, &selection, &target,
1503              &resulttype, &value, &length, &format,
1504              &size, ctx->owner_closure, (XtRequestId *) &req)) {
1505             HandleNone(widget, callback, closure, selection);
1506         }
1507         else {
1508             if (incremental) {
1509                 Boolean allSent = FALSE;
1510 
1511                 while (!allSent) {
1512                     if (ctx->notify && (value != NULL)) {
1513                         int bytelength = (int) BYTELENGTH(length, format);
1514 
1515                         /* both sides think they own this storage */
1516                         temp = __XtMalloc((unsigned) bytelength);
1517                         (void) memmove(temp, value, (size_t) bytelength);
1518                         value = temp;
1519                     }
1520                     /* use care; older clients were never warned that
1521                      * they must return a value even if length==0
1522                      */
1523                     if (value == NULL)
1524                         value = __XtMalloc((unsigned) 1);
1525                     (*callback) (widget, closure, &selection,
1526                                  &resulttype, value, &length, &format);
1527                     if (length) {
1528                         /* should owner be notified on end-of-piece?
1529                          * Spec is unclear, but non-local transfers don't.
1530                          */
1531                         (*(XtConvertSelectionIncrProc) ctx->convert)
1532                             (ctx->widget, &selection, &target,
1533                              &resulttype, &value, &length, &format,
1534                              &size, ctx->owner_closure, (XtRequestId *) &req);
1535                     }
1536                     else
1537                         allSent = TRUE;
1538                 }
1539             }
1540             else {
1541                 while (length) {
1542                     int bytelength = (int) BYTELENGTH(length, format);
1543 
1544                     total = XtRealloc(total,
1545                                       (Cardinal) (totallength =
1546                                                   totallength +
1547                                                   (unsigned long) bytelength));
1548                     (void) memmove((char *) total + totallength - bytelength,
1549                                    value, (size_t) bytelength);
1550                     (*(XtConvertSelectionIncrProc) ctx->convert)
1551                         (ctx->widget, &selection, &target,
1552                          &resulttype, &value, &length, &format,
1553                          &size, ctx->owner_closure, (XtRequestId *) &req);
1554                 }
1555                 if (total == NULL)
1556                     total = __XtMalloc(1);
1557                 totallength = NUMELEM2(totallength, format);
1558                 (*callback) (widget, closure, &selection, &resulttype,
1559                              total, &totallength, &format);
1560             }
1561             if (ctx->notify)
1562                 (*(XtSelectionDoneIncrProc) ctx->notify)
1563                     (ctx->widget, &selection, &target,
1564                      (XtRequestId *) &req, ctx->owner_closure);
1565             else
1566                 XtFree((char *) value);
1567         }
1568     }
1569     else {                      /* not incremental owner */
1570         if (!(*ctx->convert) (ctx->widget, &selection, &target,
1571                               &resulttype, &value, &length, &format)) {
1572             HandleNone(widget, callback, closure, selection);
1573         }
1574         else {
1575             if (ctx->notify && (value != NULL)) {
1576                 int bytelength = (int) BYTELENGTH(length, format);
1577 
1578                 /* both sides think they own this storage; better copy */
1579                 temp = __XtMalloc((unsigned) bytelength);
1580                 (void) memmove(temp, value, (size_t) bytelength);
1581                 value = temp;
1582             }
1583             if (value == NULL)
1584                 value = __XtMalloc((unsigned) 1);
1585             (*callback) (widget, closure, &selection, &resulttype,
1586                          value, &length, &format);
1587             if (ctx->notify)
1588                 (*ctx->notify) (ctx->widget, &selection, &target);
1589         }
1590     }
1591 }
1592 
1593 static void
GetSelectionValue(Widget widget,Atom selection,Atom target,XtSelectionCallbackProc callback,XtPointer closure,Time time,Boolean incremental,Atom property)1594 GetSelectionValue(Widget widget,
1595                   Atom selection,
1596                   Atom target,
1597                   XtSelectionCallbackProc callback,
1598                   XtPointer closure,
1599                   Time time,
1600                   Boolean incremental,
1601                   Atom property)
1602 {
1603     Select ctx;
1604     Atom properties[1];
1605 
1606     properties[0] = property;
1607 
1608     ctx = FindCtx(XtDisplay(widget), selection);
1609     if (ctx->widget && !ctx->was_disowned) {
1610         RequestRec req;
1611 
1612         ctx->req = &req;
1613         req.ctx = ctx;
1614         req.event.time = time;
1615         ctx->ref_count++;
1616         DoLocalTransfer(&req, selection, target, widget,
1617                         callback, closure, incremental, property);
1618         if (--ctx->ref_count == 0 && ctx->free_when_done)
1619             XtFree((char *) ctx);
1620         else
1621             ctx->req = NULL;
1622     }
1623     else {
1624         CallBackInfo info;
1625 
1626         info = MakeInfo(ctx, &callback, &closure, 1, widget,
1627                         time, &incremental, properties);
1628         info->target = (Atom *) __XtMalloc((unsigned) sizeof(Atom));
1629         *(info->target) = target;
1630         RequestSelectionValue(info, selection, target);
1631     }
1632 }
1633 
1634 void
XtGetSelectionValue(Widget widget,Atom selection,Atom target,XtSelectionCallbackProc callback,XtPointer closure,Time time)1635 XtGetSelectionValue(Widget widget,
1636                     Atom selection,
1637                     Atom target,
1638                     XtSelectionCallbackProc callback,
1639                     XtPointer closure,
1640                     Time time)
1641 {
1642     Atom property;
1643     Boolean incr = False;
1644 
1645     WIDGET_TO_APPCON(widget);
1646 
1647     LOCK_APP(app);
1648     property = GetParamInfo(widget, selection);
1649     RemoveParamInfo(widget, selection);
1650 
1651     if (IsGatheringRequest(widget, selection)) {
1652         AddSelectionRequests(widget, selection, 1, &target, &callback, 1,
1653                              &closure, &incr, &property);
1654     }
1655     else {
1656         GetSelectionValue(widget, selection, target, callback,
1657                           closure, time, FALSE, property);
1658     }
1659     UNLOCK_APP(app);
1660 }
1661 
1662 void
XtGetSelectionValueIncremental(Widget widget,Atom selection,Atom target,XtSelectionCallbackProc callback,XtPointer closure,Time time)1663 XtGetSelectionValueIncremental(Widget widget,
1664                                Atom selection,
1665                                Atom target,
1666                                XtSelectionCallbackProc callback,
1667                                XtPointer closure,
1668                                Time time)
1669 {
1670     Atom property;
1671     Boolean incr = TRUE;
1672 
1673     WIDGET_TO_APPCON(widget);
1674 
1675     LOCK_APP(app);
1676     property = GetParamInfo(widget, selection);
1677     RemoveParamInfo(widget, selection);
1678 
1679     if (IsGatheringRequest(widget, selection)) {
1680         AddSelectionRequests(widget, selection, 1, &target, &callback, 1,
1681                              &closure, &incr, &property);
1682     }
1683     else {
1684         GetSelectionValue(widget, selection, target, callback,
1685                           closure, time, TRUE, property);
1686     }
1687 
1688     UNLOCK_APP(app);
1689 }
1690 
1691 static void
GetSelectionValues(Widget widget,Atom selection,Atom * targets,int count,XtSelectionCallbackProc * callbacks,int num_callbacks,XtPointer * closures,Time time,Boolean * incremental,Atom * properties)1692 GetSelectionValues(Widget widget,
1693                    Atom selection,
1694                    Atom *targets,
1695                    int count,
1696                    XtSelectionCallbackProc *callbacks,
1697                    int num_callbacks,
1698                    XtPointer *closures,
1699                    Time time,
1700                    Boolean *incremental,
1701                    Atom *properties)
1702 {
1703     Select ctx;
1704     IndirectPair *pairs;
1705 
1706     if (count == 0)
1707         return;
1708     ctx = FindCtx(XtDisplay(widget), selection);
1709     if (ctx->widget && !ctx->was_disowned) {
1710         int j, i;
1711         RequestRec req;
1712 
1713         ctx->req = &req;
1714         req.ctx = ctx;
1715         req.event.time = time;
1716         ctx->ref_count++;
1717         for (i = 0, j = 0; count; count--, i++, j++) {
1718             if (j >= num_callbacks)
1719                 j = 0;
1720 
1721             DoLocalTransfer(&req, selection, targets[i], widget,
1722                             callbacks[j], closures[i], incremental[i],
1723                             properties ? properties[i] : None);
1724 
1725         }
1726         if (--ctx->ref_count == 0 && ctx->free_when_done)
1727             XtFree((char *) ctx);
1728         else
1729             ctx->req = NULL;
1730     }
1731     else {
1732         XtSelectionCallbackProc *passed_callbacks;
1733         XtSelectionCallbackProc stack_cbs[32];
1734         CallBackInfo info;
1735         IndirectPair *p;
1736         Atom *t;
1737         int i = 0, j = 0;
1738 
1739         passed_callbacks = (XtSelectionCallbackProc *)
1740             XtStackAlloc(sizeof(XtSelectionCallbackProc) * (size_t) count,
1741                          stack_cbs);
1742 
1743         /* To deal with the old calls from XtGetSelectionValues* we
1744            will repeat however many callbacks have been passed into
1745            the array */
1746         for (i = 0; i < count; i++) {
1747             if (j >= num_callbacks)
1748                 j = 0;
1749             passed_callbacks[i] = callbacks[j];
1750             j++;
1751         }
1752         info = MakeInfo(ctx, passed_callbacks, closures, count, widget,
1753                         time, incremental, properties);
1754         XtStackFree((XtPointer) passed_callbacks, stack_cbs);
1755 
1756         info->target = (Atom *)
1757             __XtMalloc((unsigned) ((size_t) (count + 1) * sizeof(Atom)));
1758         (*info->target) = ctx->prop_list->indirect_atom;
1759         (void) memmove((char *) info->target + sizeof(Atom), (char *) targets,
1760                        (size_t) count * sizeof(Atom));
1761         pairs = (IndirectPair *)
1762             __XtMalloc((unsigned) ((size_t) count * sizeof(IndirectPair)));
1763         for (p = &pairs[count - 1], t = &targets[count - 1], i = count - 1;
1764              p >= pairs; p--, t--, i--) {
1765             p->target = *t;
1766             if (properties == NULL || properties[i] == None) {
1767                 p->property = GetSelectionProperty(XtDisplay(widget));
1768                 XDeleteProperty(XtDisplay(widget), XtWindow(widget),
1769                                 p->property);
1770             }
1771             else {
1772                 p->property = properties[i];
1773             }
1774         }
1775         XChangeProperty(XtDisplay(widget), XtWindow(widget),
1776                         info->property, info->property,
1777                         32, PropModeReplace, (unsigned char *) pairs,
1778                         count * IndirectPairWordSize);
1779         XtFree((char *) pairs);
1780         RequestSelectionValue(info, selection, ctx->prop_list->indirect_atom);
1781     }
1782 }
1783 
1784 void
XtGetSelectionValues(Widget widget,Atom selection,Atom * targets,int count,XtSelectionCallbackProc callback,XtPointer * closures,Time time)1785 XtGetSelectionValues(Widget widget,
1786                      Atom selection,
1787                      Atom *targets,
1788                      int count,
1789                      XtSelectionCallbackProc callback,
1790                      XtPointer *closures,
1791                      Time time)
1792 {
1793     Boolean incremental_values[32];
1794     Boolean *incremental;
1795     int i;
1796 
1797     WIDGET_TO_APPCON(widget);
1798 
1799     LOCK_APP(app);
1800     incremental =
1801         XtStackAlloc((size_t) count * sizeof(Boolean), incremental_values);
1802     for (i = 0; i < count; i++)
1803         incremental[i] = FALSE;
1804     if (IsGatheringRequest(widget, selection)) {
1805         AddSelectionRequests(widget, selection, count, targets, &callback,
1806                              1, closures, incremental, NULL);
1807     }
1808     else {
1809         GetSelectionValues(widget, selection, targets, count, &callback, 1,
1810                            closures, time, incremental, NULL);
1811     }
1812     XtStackFree((XtPointer) incremental, incremental_values);
1813     UNLOCK_APP(app);
1814 }
1815 
1816 void
XtGetSelectionValuesIncremental(Widget widget,Atom selection,Atom * targets,int count,XtSelectionCallbackProc callback,XtPointer * closures,Time time)1817 XtGetSelectionValuesIncremental(Widget widget,
1818                                 Atom selection,
1819                                 Atom *targets,
1820                                 int count,
1821                                 XtSelectionCallbackProc callback,
1822                                 XtPointer *closures,
1823                                 Time time)
1824 {
1825     Boolean incremental_values[32];
1826     Boolean *incremental;
1827     int i;
1828 
1829     WIDGET_TO_APPCON(widget);
1830 
1831     LOCK_APP(app);
1832     incremental =
1833         XtStackAlloc((size_t) count * sizeof(Boolean), incremental_values);
1834     for (i = 0; i < count; i++)
1835         incremental[i] = TRUE;
1836     if (IsGatheringRequest(widget, selection)) {
1837         AddSelectionRequests(widget, selection, count, targets, &callback,
1838                              1, closures, incremental, NULL);
1839     }
1840     else {
1841         GetSelectionValues(widget, selection, targets, count,
1842                            &callback, 1, closures, time, incremental, NULL);
1843     }
1844     XtStackFree((XtPointer) incremental, incremental_values);
1845     UNLOCK_APP(app);
1846 }
1847 
1848 static Request
GetRequestRecord(Widget widget,Atom selection,XtRequestId id)1849 GetRequestRecord(Widget widget, Atom selection, XtRequestId id)
1850 {
1851     Request req = (Request) id;
1852     Select ctx = NULL;
1853 
1854     if ((req == NULL
1855          && ((ctx = FindCtx(XtDisplay(widget), selection)) == NULL
1856              || ctx->req == NULL
1857              || ctx->selection != selection || ctx->widget == NULL))
1858         || (req != NULL
1859             && (req->ctx == NULL
1860                 || req->ctx->selection != selection
1861                 || req->ctx->widget != widget))) {
1862         String params = XtName(widget);
1863         Cardinal num_params = 1;
1864 
1865         XtAppWarningMsg(XtWidgetToApplicationContext(widget),
1866                         "notInConvertSelection", "xtGetSelectionRequest",
1867                         XtCXtToolkitError,
1868                         "XtGetSelectionRequest or XtGetSelectionParameters called for widget \"%s\" outside of ConvertSelection proc",
1869                         &params, &num_params);
1870         return NULL;
1871     }
1872 
1873     if (req == NULL) {
1874         /* non-incremental owner; only one request can be
1875          * outstanding at a time, so it's safe to keep ptr in ctx */
1876         req = ctx->req;
1877     }
1878     return req;
1879 }
1880 
1881 XSelectionRequestEvent *
XtGetSelectionRequest(Widget widget,Atom selection,XtRequestId id)1882 XtGetSelectionRequest(Widget widget, Atom selection, XtRequestId id)
1883 {
1884     Request req = (Request) id;
1885 
1886     WIDGET_TO_APPCON(widget);
1887 
1888     LOCK_APP(app);
1889 
1890     req = GetRequestRecord(widget, selection, id);
1891 
1892     if (!req) {
1893         UNLOCK_APP(app);
1894         return (XSelectionRequestEvent *) NULL;
1895     }
1896 
1897     if (req->event.type == 0) {
1898         /* owner is local; construct the remainder of the event */
1899         req->event.type = SelectionRequest;
1900         req->event.serial = LastKnownRequestProcessed(XtDisplay(widget));
1901         req->event.send_event = True;
1902         req->event.display = XtDisplay(widget);
1903 
1904         req->event.owner = XtWindow(req->ctx->widget);
1905         req->event.selection = selection;
1906     }
1907     UNLOCK_APP(app);
1908     return &req->event;
1909 }
1910 
1911 /* Property atom access */
1912 Atom
XtReservePropertyAtom(Widget w)1913 XtReservePropertyAtom(Widget w)
1914 {
1915     return (GetSelectionProperty(XtDisplay(w)));
1916 }
1917 
1918 void
XtReleasePropertyAtom(Widget w,Atom atom)1919 XtReleasePropertyAtom(Widget w, Atom atom)
1920 {
1921     FreeSelectionProperty(XtDisplay(w), atom);
1922 }
1923 
1924 /* Multiple utilities */
1925 
1926 /* All requests are put in a single list per widget.  It is
1927    very unlikely anyone will be gathering multiple MULTIPLE
1928    requests at the same time,  so the loss in efficiency for
1929    this case is acceptable */
1930 
1931 /* Queue one or more requests to the one we're gathering */
1932 static void
AddSelectionRequests(Widget wid,Atom sel,int count,Atom * targets,XtSelectionCallbackProc * callbacks,int num_cb,XtPointer * closures,Boolean * incrementals,Atom * properties)1933 AddSelectionRequests(Widget wid,
1934                      Atom sel,
1935                      int count,
1936                      Atom *targets,
1937                      XtSelectionCallbackProc *callbacks,
1938                      int num_cb,
1939                      XtPointer *closures,
1940                      Boolean *incrementals,
1941                      Atom *properties)
1942 {
1943     QueuedRequestInfo qi;
1944     Window window = XtWindow(wid);
1945     Display *dpy = XtDisplay(wid);
1946 
1947     LOCK_PROCESS;
1948     if (multipleContext == 0)
1949         multipleContext = XUniqueContext();
1950 
1951     qi = NULL;
1952     (void) XFindContext(dpy, window, multipleContext, (XPointer *) &qi);
1953 
1954     if (qi != NULL) {
1955         QueuedRequest *req = qi->requests;
1956         int start = qi->count;
1957         int i = 0;
1958         int j = 0;
1959 
1960         qi->count += count;
1961         req = (QueuedRequest *) XtRealloc((char *) req,
1962                                           (Cardinal) ((size_t) (start + count) *
1963                                                       sizeof(QueuedRequest)));
1964         while (i < count) {
1965             QueuedRequest newreq = (QueuedRequest)
1966                 __XtMalloc(sizeof(QueuedRequestRec));
1967 
1968             newreq->selection = sel;
1969             newreq->target = targets[i];
1970             if (properties != NULL)
1971                 newreq->param = properties[i];
1972             else {
1973                 newreq->param = GetSelectionProperty(dpy);
1974                 XDeleteProperty(dpy, window, newreq->param);
1975             }
1976             newreq->callback = callbacks[j];
1977             newreq->closure = closures[i];
1978             newreq->incremental = incrementals[i];
1979 
1980             req[start] = newreq;
1981             start++;
1982             i++;
1983             j++;
1984             if (j > num_cb)
1985                 j = 0;
1986         }
1987 
1988         qi->requests = req;
1989     }
1990     else {
1991         /* Impossible */
1992     }
1993 
1994     UNLOCK_PROCESS;
1995 }
1996 
1997 /* Only call IsGatheringRequest when we have a lock already */
1998 
1999 static Boolean
IsGatheringRequest(Widget wid,Atom sel)2000 IsGatheringRequest(Widget wid, Atom sel)
2001 {
2002     QueuedRequestInfo qi;
2003     Window window = XtWindow(wid);
2004     Display *dpy = XtDisplay(wid);
2005     Boolean found = False;
2006 
2007     if (multipleContext == 0)
2008         multipleContext = XUniqueContext();
2009 
2010     qi = NULL;
2011     (void) XFindContext(dpy, window, multipleContext, (XPointer *) &qi);
2012 
2013     if (qi != NULL) {
2014         int i = 0;
2015 
2016         while (qi->selections[i] != None) {
2017             if (qi->selections[i] == sel) {
2018                 found = True;
2019                 break;
2020             }
2021             i++;
2022         }
2023     }
2024 
2025     return (found);
2026 }
2027 
2028 /* Cleanup request scans the request queue and releases any
2029    properties queued, and removes any requests queued */
2030 static void
CleanupRequest(Display * dpy,QueuedRequestInfo qi,Atom sel)2031 CleanupRequest(Display *dpy, QueuedRequestInfo qi, Atom sel)
2032 {
2033     int i, j, n;
2034 
2035     i = 0;
2036 
2037     /* Remove this selection from the list */
2038     n = 0;
2039     while (qi->selections[n] != sel && qi->selections[n] != None)
2040         n++;
2041     if (qi->selections[n] == sel) {
2042         while (qi->selections[n] != None) {
2043             qi->selections[n] = qi->selections[n + 1];
2044             n++;
2045         }
2046     }
2047 
2048     while (i < qi->count) {
2049         QueuedRequest req = qi->requests[i];
2050 
2051         if (req->selection == sel) {
2052             /* Match */
2053             if (req->param != None)
2054                 FreeSelectionProperty(dpy, req->param);
2055             qi->count--;
2056 
2057             for (j = i; j < qi->count; j++)
2058                 qi->requests[j] = qi->requests[j + 1];
2059 
2060             XtFree((char *) req);
2061         }
2062         else {
2063             i++;
2064         }
2065     }
2066 }
2067 
2068 void
XtCreateSelectionRequest(Widget widget,Atom selection)2069 XtCreateSelectionRequest(Widget widget, Atom selection)
2070 {
2071     QueuedRequestInfo queueInfo;
2072     Window window = XtWindow(widget);
2073     Display *dpy = XtDisplay(widget);
2074     int n;
2075 
2076     LOCK_PROCESS;
2077     if (multipleContext == 0)
2078         multipleContext = XUniqueContext();
2079 
2080     queueInfo = NULL;
2081     (void) XFindContext(dpy, window, multipleContext, (XPointer *) &queueInfo);
2082 
2083     /* If there is one,  then cancel it */
2084     if (queueInfo != NULL)
2085         CleanupRequest(dpy, queueInfo, selection);
2086     else {
2087         /* Create it */
2088         queueInfo =
2089             (QueuedRequestInfo) __XtMalloc(sizeof(QueuedRequestInfoRec));
2090         queueInfo->count = 0;
2091         queueInfo->selections = (Atom *) __XtMalloc(sizeof(Atom) * 2);
2092         queueInfo->selections[0] = None;
2093         queueInfo->requests = (QueuedRequest *)
2094             __XtMalloc(sizeof(QueuedRequest));
2095     }
2096 
2097     /* Append this selection to list */
2098     n = 0;
2099     while (queueInfo->selections[n] != None)
2100         n++;
2101     queueInfo->selections =
2102         (Atom *) XtRealloc((char *) queueInfo->selections,
2103                            (Cardinal) ((size_t) (n + 2) * sizeof(Atom)));
2104     queueInfo->selections[n] = selection;
2105     queueInfo->selections[n + 1] = None;
2106 
2107     (void) XSaveContext(dpy, window, multipleContext, (char *) queueInfo);
2108     UNLOCK_PROCESS;
2109 }
2110 
2111 void
XtSendSelectionRequest(Widget widget,Atom selection,Time time)2112 XtSendSelectionRequest(Widget widget, Atom selection, Time time)
2113 {
2114     QueuedRequestInfo queueInfo;
2115     Window window = XtWindow(widget);
2116     Display *dpy = XtDisplay(widget);
2117 
2118     LOCK_PROCESS;
2119     if (multipleContext == 0)
2120         multipleContext = XUniqueContext();
2121 
2122     queueInfo = NULL;
2123     (void) XFindContext(dpy, window, multipleContext, (XPointer *) &queueInfo);
2124     if (queueInfo != NULL) {
2125         int i;
2126         int count = 0;
2127         QueuedRequest *req = queueInfo->requests;
2128 
2129         /* Construct the requests and send it using
2130            GetSelectionValues */
2131         for (i = 0; i < queueInfo->count; i++)
2132             if (req[i]->selection == selection)
2133                 count++;
2134 
2135         if (count > 0) {
2136             if (count == 1) {
2137                 for (i = 0; i < queueInfo->count; i++)
2138                     if (req[i]->selection == selection)
2139                         break;
2140 
2141                 /* special case a multiple which isn't needed */
2142                 GetSelectionValue(widget, selection, req[i]->target,
2143                                   req[i]->callback, req[i]->closure, time,
2144                                   req[i]->incremental, req[i]->param);
2145             }
2146             else {
2147                 Atom *targets;
2148                 Atom t[PREALLOCED];
2149                 XtSelectionCallbackProc *cbs;
2150                 XtSelectionCallbackProc c[PREALLOCED];
2151                 XtPointer *closures;
2152                 XtPointer cs[PREALLOCED];
2153                 Boolean *incrs;
2154                 Boolean ins[PREALLOCED];
2155                 Atom *props;
2156                 Atom p[PREALLOCED];
2157                 int j = 0;
2158 
2159                 /* Allocate */
2160                 targets =
2161                     (Atom *) XtStackAlloc((size_t) count * sizeof(Atom), t);
2162                 cbs = (XtSelectionCallbackProc *)
2163                     XtStackAlloc((size_t) count *
2164                                  sizeof(XtSelectionCallbackProc), c);
2165                 closures =
2166                     (XtPointer *) XtStackAlloc((size_t) count *
2167                                                sizeof(XtPointer), cs);
2168                 incrs =
2169                     (Boolean *) XtStackAlloc((size_t) count * sizeof(Boolean),
2170                                              ins);
2171                 props = (Atom *) XtStackAlloc((size_t) count * sizeof(Atom), p);
2172 
2173                 /* Copy */
2174                 for (i = 0; i < queueInfo->count; i++) {
2175                     if (req[i]->selection == selection) {
2176                         targets[j] = req[i]->target;
2177                         cbs[j] = req[i]->callback;
2178                         closures[j] = req[i]->closure;
2179                         incrs[j] = req[i]->incremental;
2180                         props[j] = req[i]->param;
2181                         j++;
2182                     }
2183                 }
2184 
2185                 /* Make the request */
2186                 GetSelectionValues(widget, selection, targets, count,
2187                                    cbs, count, closures, time, incrs, props);
2188 
2189                 /* Free */
2190                 XtStackFree((XtPointer) targets, t);
2191                 XtStackFree((XtPointer) cbs, c);
2192                 XtStackFree((XtPointer) closures, cs);
2193                 XtStackFree((XtPointer) incrs, ins);
2194                 XtStackFree((XtPointer) props, p);
2195             }
2196         }
2197     }
2198 
2199     CleanupRequest(dpy, queueInfo, selection);
2200     UNLOCK_PROCESS;
2201 }
2202 
2203 void
XtCancelSelectionRequest(Widget widget,Atom selection)2204 XtCancelSelectionRequest(Widget widget, Atom selection)
2205 {
2206     QueuedRequestInfo queueInfo;
2207     Window window = XtWindow(widget);
2208     Display *dpy = XtDisplay(widget);
2209 
2210     LOCK_PROCESS;
2211     if (multipleContext == 0)
2212         multipleContext = XUniqueContext();
2213 
2214     queueInfo = NULL;
2215     (void) XFindContext(dpy, window, multipleContext, (XPointer *) &queueInfo);
2216     /* If there is one,  then cancel it */
2217     if (queueInfo != NULL)
2218         CleanupRequest(dpy, queueInfo, selection);
2219     UNLOCK_PROCESS;
2220 }
2221 
2222 /* Parameter utilities */
2223 
2224 /* Parameters on a selection request */
2225 /* Places data on allocated parameter atom,  then records the
2226    parameter atom data for use in the next call to one of
2227    the XtGetSelectionValue functions. */
2228 void
XtSetSelectionParameters(Widget requestor,Atom selection,Atom type,XtPointer value,unsigned long length,int format)2229 XtSetSelectionParameters(Widget requestor,
2230                          Atom selection,
2231                          Atom type,
2232                          XtPointer value,
2233                          unsigned long length,
2234                          int format)
2235 {
2236     Display *dpy = XtDisplay(requestor);
2237     Window window = XtWindow(requestor);
2238     Atom property = GetParamInfo(requestor, selection);
2239 
2240     if (property == None) {
2241         property = GetSelectionProperty(dpy);
2242         AddParamInfo(requestor, selection, property);
2243     }
2244 
2245     XChangeProperty(dpy, window, property,
2246                     type, format, PropModeReplace,
2247                     (unsigned char *) value, (int) length);
2248 }
2249 
2250 /* Retrieves data passed in a parameter. Data for this is stored
2251    on the originator's window */
2252 void
XtGetSelectionParameters(Widget owner,Atom selection,XtRequestId request_id,Atom * type_return,XtPointer * value_return,unsigned long * length_return,int * format_return)2253 XtGetSelectionParameters(Widget owner,
2254                          Atom selection,
2255                          XtRequestId request_id,
2256                          Atom *type_return,
2257                          XtPointer *value_return,
2258                          unsigned long *length_return,
2259                          int *format_return)
2260 {
2261     Request req;
2262     Display *dpy = XtDisplay(owner);
2263 
2264     WIDGET_TO_APPCON(owner);
2265 
2266     *value_return = NULL;
2267     *length_return = (unsigned long) (*format_return = 0);
2268     *type_return = None;
2269 
2270     LOCK_APP(app);
2271 
2272     req = GetRequestRecord(owner, selection, request_id);
2273 
2274     if (req && req->property) {
2275         unsigned long bytes_after;      /* unused */
2276 
2277         StartProtectedSection(dpy, req->requestor);
2278         XGetWindowProperty(dpy, req->requestor, req->property, 0L, 10000000,
2279                            False, AnyPropertyType, type_return, format_return,
2280                            length_return, &bytes_after,
2281                            (unsigned char **) value_return);
2282         EndProtectedSection(dpy);
2283 #ifdef XT_COPY_SELECTION
2284         if (*value_return) {
2285             int size = (int) BYTELENGTH(*length_return, *format_return) + 1;
2286             char *tmp = __XtMalloc((Cardinal) size);
2287 
2288             (void) memmove(tmp, *value_return, (size_t) size);
2289             XFree(*value_return);
2290             *value_return = tmp;
2291         }
2292 #endif
2293     }
2294     UNLOCK_APP(app);
2295 }
2296 
2297 /*  Parameters are temporarily stashed in an XContext.  A list is used because
2298  *  there may be more than one selection request in progress.  The context
2299  *  data is deleted when the list is empty.  In the future, the parameter
2300  *  context could be merged with other contexts used during selections.
2301  */
2302 
2303 static void
AddParamInfo(Widget w,Atom selection,Atom param_atom)2304 AddParamInfo(Widget w, Atom selection, Atom param_atom)
2305 {
2306     Param p;
2307     ParamInfo pinfo;
2308 
2309     LOCK_PROCESS;
2310     if (paramPropertyContext == 0)
2311         paramPropertyContext = XUniqueContext();
2312 
2313     if (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
2314                      (XPointer *) &pinfo)) {
2315         pinfo = (ParamInfo) __XtMalloc(sizeof(ParamInfoRec));
2316         pinfo->count = 1;
2317         pinfo->paramlist = XtNew(ParamRec);
2318         p = pinfo->paramlist;
2319         (void) XSaveContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
2320                             (char *) pinfo);
2321     }
2322     else {
2323         int n;
2324 
2325         for (n = (int) pinfo->count, p = pinfo->paramlist; n; n--, p++) {
2326             if (p->selection == None || p->selection == selection)
2327                 break;
2328         }
2329         if (n == 0) {
2330             pinfo->count++;
2331             pinfo->paramlist = (Param)
2332                 XtRealloc((char *) pinfo->paramlist,
2333                           (Cardinal) (pinfo->count * sizeof(ParamRec)));
2334             p = &pinfo->paramlist[pinfo->count - 1];
2335             (void) XSaveContext(XtDisplay(w), XtWindow(w),
2336                                 paramPropertyContext, (char *) pinfo);
2337         }
2338     }
2339     p->selection = selection;
2340     p->param = param_atom;
2341     UNLOCK_PROCESS;
2342 }
2343 
2344 static void
RemoveParamInfo(Widget w,Atom selection)2345 RemoveParamInfo(Widget w, Atom selection)
2346 {
2347     ParamInfo pinfo;
2348     Boolean retain = False;
2349 
2350     LOCK_PROCESS;
2351     if (paramPropertyContext
2352         && (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
2353                          (XPointer *) &pinfo) == 0)) {
2354         Param p;
2355         int n;
2356 
2357         /* Find and invalidate the parameter data. */
2358         for (n = (int) pinfo->count, p = pinfo->paramlist; n; n--, p++) {
2359             if (p->selection != None) {
2360                 if (p->selection == selection)
2361                     p->selection = None;
2362                 else
2363                     retain = True;
2364             }
2365         }
2366         /* If there's no valid data remaining, release the context entry. */
2367         if (!retain) {
2368             XtFree((char *) pinfo->paramlist);
2369             XtFree((char *) pinfo);
2370             XDeleteContext(XtDisplay(w), XtWindow(w), paramPropertyContext);
2371         }
2372     }
2373     UNLOCK_PROCESS;
2374 }
2375 
2376 static Atom
GetParamInfo(Widget w,Atom selection)2377 GetParamInfo(Widget w, Atom selection)
2378 {
2379     ParamInfo pinfo;
2380     Atom atom = None;
2381 
2382     LOCK_PROCESS;
2383     if (paramPropertyContext
2384         && (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
2385                          (XPointer *) &pinfo) == 0)) {
2386         Param p;
2387         int n;
2388 
2389         for (n = (int) pinfo->count, p = pinfo->paramlist; n; n--, p++)
2390             if (p->selection == selection) {
2391                 atom = p->param;
2392                 break;
2393             }
2394     }
2395     UNLOCK_PROCESS;
2396     return atom;
2397 }
2398