1 /*
2  * tkSelect.c --
3  *
4  *	This file manages the selection for the Tk toolkit, translating
5  *	between the standard X ICCCM conventions and Tcl commands.
6  *
7  * Copyright © 1990-1993 The Regents of the University of California.
8  * Copyright © 1994-1997 Sun Microsystems, Inc.
9  *
10  * See the file "license.terms" for information on usage and redistribution of
11  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12  */
13 
14 #include "tkInt.h"
15 #include "tkSelect.h"
16 
17 /*
18  * When a selection handler is set up by invoking "selection handle", one of
19  * the following data structures is set up to hold information about the
20  * command to invoke and its interpreter.
21  */
22 
23 typedef struct {
24     Tcl_Interp *interp;		/* Interpreter in which to invoke command. */
25     int cmdLength;		/* # of non-NULL bytes in command. */
26     int charOffset;		/* The offset of the next char to retrieve. */
27     int byteOffset;		/* The expected byte offset of the next
28 				 * chunk. */
29     char buffer[4];		/* A buffer to hold part of a UTF character
30 				 * that is split across chunks. */
31     char command[TKFLEXARRAY];		/* Command to invoke. Actual space is
32 				 * allocated as large as necessary. This must
33 				 * be the last entry in the structure. */
34 } CommandInfo;
35 
36 /*
37  * When selection ownership is claimed with the "selection own" Tcl command,
38  * one of the following structures is created to record the Tcl command to be
39  * executed when the selection is lost again.
40  */
41 
42 typedef struct LostCommand {
43     Tcl_Interp *interp;		/* Interpreter in which to invoke command. */
44     Tcl_Obj *cmdObj;		/* Reference to command to invoke. */
45 } LostCommand;
46 
47 /*
48  * The structure below is used to keep each thread's pending list separate.
49  */
50 
51 typedef struct {
52     TkSelInProgress *pendingPtr;
53 				/* Topmost search in progress, or NULL if
54 				 * none. */
55 } ThreadSpecificData;
56 static Tcl_ThreadDataKey dataKey;
57 
58 /*
59  * Forward declarations for functions defined in this file:
60  */
61 
62 static TkSizeT	HandleTclCommand(ClientData clientData,
63 			    TkSizeT offset, char *buffer, TkSizeT maxBytes);
64 static void		LostSelection(ClientData clientData);
65 static int		SelGetProc(ClientData clientData,
66 			    Tcl_Interp *interp, const char *portion);
67 
68 /*
69  *--------------------------------------------------------------
70  *
71  * Tk_CreateSelHandler --
72  *
73  *	This function is called to register a function as the handler for
74  *	selection requests of a particular target type on a particular window
75  *	for a particular selection.
76  *
77  * Results:
78  *	None.
79  *
80  * Side effects:
81 
82  *	In the future, whenever the selection is in tkwin's window and someone
83  *	requests the selection in the form given by target, proc will be
84  *	invoked to provide part or all of the selection in the given form. If
85  *	there was already a handler declared for the given window, target and
86  *	selection type, then it is replaced. Proc should have the following
87  *	form:
88  *
89  *	int
90  *	proc(
91  *	    ClientData clientData,
92  *	    int offset,
93  *	    char *buffer,
94  *	    int maxBytes)
95  *	{
96  *	}
97  *
98  *	The clientData argument to proc will be the same as the clientData
99  *	argument to this function. The offset argument indicates which portion
100  *	of the selection to return: skip the first offset bytes. Buffer is a
101  *	pointer to an area in which to place the converted selection, and
102  *	maxBytes gives the number of bytes available at buffer. Proc should
103  *	place the selection in buffer as a string, and return a count of the
104  *	number of bytes of selection actually placed in buffer (not including
105  *	the terminating NULL character). If the return value equals maxBytes,
106  *	this is a sign that there is probably still more selection information
107  *	available.
108  *
109  *--------------------------------------------------------------
110  */
111 
112 void
Tk_CreateSelHandler(Tk_Window tkwin,Atom selection,Atom target,Tk_SelectionProc * proc,ClientData clientData,Atom format)113 Tk_CreateSelHandler(
114     Tk_Window tkwin,		/* Token for window. */
115     Atom selection,		/* Selection to be handled. */
116     Atom target,		/* The kind of selection conversions that can
117 				 * be handled by proc, e.g. TARGETS or
118 				 * STRING. */
119     Tk_SelectionProc *proc,	/* Function to invoke to convert selection to
120 				 * type "target". */
121     ClientData clientData,	/* Value to pass to proc. */
122     Atom format)		/* Format in which the selection information
123 				 * should be returned to the requestor.
124 				 * XA_STRING is best by far, but anything
125 				 * listed in the ICCCM will be tolerated
126 				 * (blech). */
127 {
128     TkSelHandler *selPtr;
129     TkWindow *winPtr = (TkWindow *) tkwin;
130 
131     if (winPtr->dispPtr->multipleAtom == None) {
132 	TkSelInit(tkwin);
133     }
134 
135     /*
136      * See if there's already a handler for this target and selection on this
137      * window. If so, re-use it. If not, create a new one.
138      */
139 
140     for (selPtr = winPtr->selHandlerList; ; selPtr = selPtr->nextPtr) {
141 	if (selPtr == NULL) {
142 	    selPtr = (TkSelHandler *)ckalloc(sizeof(TkSelHandler));
143 	    selPtr->nextPtr = winPtr->selHandlerList;
144 	    winPtr->selHandlerList = selPtr;
145 	    break;
146 	}
147 	if ((selPtr->selection == selection) && (selPtr->target == target)) {
148 	    /*
149 	     * Special case: when replacing handler created by "selection
150 	     * handle", free up memory. Should there be a callback to allow
151 	     * other clients to do this too?
152 	     */
153 
154 	    if (selPtr->proc == HandleTclCommand) {
155 		ckfree(selPtr->clientData);
156 	    }
157 	    break;
158 	}
159     }
160     selPtr->selection = selection;
161     selPtr->target = target;
162     selPtr->format = format;
163     selPtr->proc = proc;
164     selPtr->clientData = clientData;
165     if (format == XA_STRING) {
166 	selPtr->size = 8;
167     } else {
168 	selPtr->size = 32;
169     }
170 
171     if ((target == XA_STRING) && (winPtr->dispPtr->utf8Atom != (Atom) 0)) {
172 	/*
173 	 * If the user asked for a STRING handler and we understand
174 	 * UTF8_STRING, we implicitly create a UTF8_STRING handler for them.
175 	 */
176 
177 	target = winPtr->dispPtr->utf8Atom;
178 	for (selPtr = winPtr->selHandlerList; ; selPtr = selPtr->nextPtr) {
179 	    if (selPtr == NULL) {
180 		selPtr = (TkSelHandler *)ckalloc(sizeof(TkSelHandler));
181 		selPtr->nextPtr = winPtr->selHandlerList;
182 		winPtr->selHandlerList = selPtr;
183 		selPtr->selection = selection;
184 		selPtr->target = target;
185 		selPtr->format = target; /* We want UTF8_STRING format */
186 		selPtr->proc = proc;
187 		if (selPtr->proc == HandleTclCommand) {
188 		    /*
189 		     * The clientData is selection controlled memory, so we
190 		     * should make a copy for this selPtr.
191 		     */
192 
193 		    size_t cmdInfoLen = offsetof(CommandInfo, command) + 1 +
194 			    ((CommandInfo *)clientData)->cmdLength;
195 
196 		    selPtr->clientData = ckalloc(cmdInfoLen);
197 		    memcpy(selPtr->clientData, clientData, cmdInfoLen);
198 		} else {
199 		    selPtr->clientData = clientData;
200 		}
201 		selPtr->size = 8;
202 		break;
203 	    }
204 	    if (selPtr->selection==selection && selPtr->target==target) {
205 		/*
206 		 * Looks like we had a utf-8 target already. Leave it alone.
207 		 */
208 
209 		break;
210 	    }
211 	}
212     }
213 }
214 
215 /*
216  *----------------------------------------------------------------------
217  *
218  * Tk_DeleteSelHandler --
219  *
220  *	Remove the selection handler for a given window, target, and
221  *	selection, if it exists.
222  *
223  * Results:
224  *	None.
225  *
226  * Side effects:
227  *	The selection handler for tkwin and target is removed. If there is no
228  *	such handler then nothing happens.
229  *
230  *----------------------------------------------------------------------
231  */
232 
233 void
Tk_DeleteSelHandler(Tk_Window tkwin,Atom selection,Atom target)234 Tk_DeleteSelHandler(
235     Tk_Window tkwin,		/* Token for window. */
236     Atom selection,		/* The selection whose handler is to be
237 				 * removed. */
238     Atom target)		/* The target whose selection handler is to be
239 				 * removed. */
240 {
241     TkWindow *winPtr = (TkWindow *) tkwin;
242     TkSelHandler *selPtr, *prevPtr;
243     TkSelInProgress *ipPtr;
244     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
245 	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
246 
247     /*
248      * Find the selection handler to be deleted, or return if it doesn't
249      * exist.
250      */
251 
252     for (selPtr = winPtr->selHandlerList, prevPtr = NULL; ;
253 	    prevPtr = selPtr, selPtr = selPtr->nextPtr) {
254 	if (selPtr == NULL) {
255 	    return;
256 	}
257 	if ((selPtr->selection == selection) && (selPtr->target == target)) {
258 	    break;
259 	}
260     }
261 
262     /*
263      * If ConvertSelection is processing this handler, tell it that the
264      * handler is dead.
265      */
266 
267     for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL;
268 	    ipPtr = ipPtr->nextPtr) {
269 	if (ipPtr->selPtr == selPtr) {
270 	    ipPtr->selPtr = NULL;
271 	}
272     }
273 
274     /*
275      * Free resources associated with the handler.
276      */
277 
278     if (prevPtr == NULL) {
279 	winPtr->selHandlerList = selPtr->nextPtr;
280     } else {
281 	prevPtr->nextPtr = selPtr->nextPtr;
282     }
283 
284     if ((target == XA_STRING) && (winPtr->dispPtr->utf8Atom != (Atom) 0)) {
285 	/*
286 	 * If the user asked for a STRING handler and we understand
287 	 * UTF8_STRING, we may have implicitly created a UTF8_STRING handler
288 	 * for them. Look for it and delete it as necessary.
289 	 */
290 
291 	TkSelHandler *utf8selPtr;
292 
293 	target = winPtr->dispPtr->utf8Atom;
294 	for (utf8selPtr = winPtr->selHandlerList; utf8selPtr != NULL;
295 		utf8selPtr = utf8selPtr->nextPtr) {
296 	    if ((utf8selPtr->selection == selection)
297 		    && (utf8selPtr->target == target)) {
298 		break;
299 	    }
300 	}
301 	if (utf8selPtr != NULL) {
302 	    if ((utf8selPtr->format == target)
303 		    && (utf8selPtr->proc == selPtr->proc)
304 		    && (utf8selPtr->size == selPtr->size)) {
305 		/*
306 		 * This recursive call is OK, because we've changed the value
307 		 * of 'target'.
308 		 */
309 
310 		Tk_DeleteSelHandler(tkwin, selection, target);
311 	    }
312 	}
313     }
314 
315     if (selPtr->proc == HandleTclCommand) {
316 	/*
317 	 * Mark the CommandInfo as deleted and free it if we can.
318 	 */
319 
320 	((CommandInfo *) selPtr->clientData)->interp = NULL;
321 	Tcl_EventuallyFree(selPtr->clientData, TCL_DYNAMIC);
322     }
323     ckfree(selPtr);
324 }
325 
326 /*
327  *--------------------------------------------------------------
328  *
329  * Tk_OwnSelection --
330  *
331  *	Arrange for tkwin to become the owner of a selection.
332  *
333  * Results:
334  *	None.
335  *
336  * Side effects:
337  *	From now on, requests for the selection will be directed to functions
338  *	associated with tkwin (they must have been declared with calls to
339  *	Tk_CreateSelHandler). When the selection is lost by this window, proc
340  *	will be invoked (see the manual entry for details). This function may
341  *	invoke callbacks, including Tcl scripts, so any calling function
342  *	should be reentrant at the point where Tk_OwnSelection is invoked.
343  *
344  *--------------------------------------------------------------
345  */
346 
347 void
Tk_OwnSelection(Tk_Window tkwin,Atom selection,Tk_LostSelProc * proc,ClientData clientData)348 Tk_OwnSelection(
349     Tk_Window tkwin,		/* Window to become new selection owner. */
350     Atom selection,		/* Selection that window should own. */
351     Tk_LostSelProc *proc,	/* Function to call when selection is taken
352 				 * away from tkwin. */
353     ClientData clientData)	/* Arbitrary one-word argument to pass to
354 				 * proc. */
355 {
356     TkWindow *winPtr = (TkWindow *) tkwin;
357     TkDisplay *dispPtr = winPtr->dispPtr;
358     TkSelectionInfo *infoPtr;
359     Tk_LostSelProc *clearProc = NULL;
360     void *clearData = NULL;/* Initialization needed only to prevent
361 				 * compiler warning. */
362 
363     if (dispPtr->multipleAtom == None) {
364 	TkSelInit(tkwin);
365     }
366     Tk_MakeWindowExist(tkwin);
367 
368     /*
369      * This code is somewhat tricky. First, we find the specified selection on
370      * the selection list. If the previous owner is in this process, and is a
371      * different window, then we need to invoke the clearProc. However, it's
372      * dangerous to call the clearProc right now, because it could invoke a
373      * Tcl script that wrecks the current state (e.g. it could delete the
374      * window). To be safe, defer the call until the end of the function when
375      * we no longer care about the state.
376      */
377 
378     for (infoPtr = dispPtr->selectionInfoPtr; infoPtr != NULL;
379 	    infoPtr = infoPtr->nextPtr) {
380 	if (infoPtr->selection == selection) {
381 	    break;
382 	}
383     }
384     if (infoPtr == NULL) {
385 	infoPtr = (TkSelectionInfo *)ckalloc(sizeof(TkSelectionInfo));
386 	infoPtr->selection = selection;
387 	infoPtr->nextPtr = dispPtr->selectionInfoPtr;
388 	dispPtr->selectionInfoPtr = infoPtr;
389     } else if (infoPtr->clearProc != NULL) {
390 	if (infoPtr->owner != tkwin) {
391 	    clearProc = infoPtr->clearProc;
392 	    clearData = infoPtr->clearData;
393 	} else if (infoPtr->clearProc == LostSelection) {
394 	    /*
395 	     * If the selection handler is one created by "selection own", be
396 	     * sure to free the record for it; otherwise there will be a
397 	     * memory leak.
398 	     */
399 
400 	    ckfree(infoPtr->clearData);
401 	}
402     }
403 
404     infoPtr->owner = tkwin;
405     infoPtr->serial = NextRequest(winPtr->display);
406     infoPtr->clearProc = proc;
407     infoPtr->clearData = clientData;
408 
409     /*
410      * Note that we are using CurrentTime, even though ICCCM recommends
411      * against this practice (the problem is that we don't necessarily have a
412      * valid time to use). We will not be able to retrieve a useful timestamp
413      * for the TIMESTAMP target later.
414      */
415 
416     infoPtr->time = CurrentTime;
417 
418     /*
419      * Note that we are not checking to see if the selection claim succeeded.
420      * If the ownership does not change, then the clearProc may never be
421      * invoked, and we will return incorrect information when queried for the
422      * current selection owner.
423      */
424 
425     XSetSelectionOwner(winPtr->display, infoPtr->selection, winPtr->window,
426 	    infoPtr->time);
427 
428     /*
429      * Now that we are done, we can invoke clearProc without running into
430      * reentrancy problems.
431      */
432 
433     if (clearProc != NULL) {
434 	clearProc(clearData);
435     }
436 }
437 
438 /*
439  *----------------------------------------------------------------------
440  *
441  * Tk_ClearSelection --
442  *
443  *	Eliminate the specified selection on tkwin's display, if there is one.
444  *
445  * Results:
446  *	None.
447  *
448  * Side effects:
449  *	The specified selection is cleared, so that future requests to
450  *	retrieve it will fail until some application owns it again. This
451  *	function invokes callbacks, possibly including Tcl scripts, so any
452  *	calling function should be reentrant at the point Tk_ClearSelection is
453  *	invoked.
454  *
455  *----------------------------------------------------------------------
456  */
457 
458 void
Tk_ClearSelection(Tk_Window tkwin,Atom selection)459 Tk_ClearSelection(
460     Tk_Window tkwin,		/* Window that selects a display. */
461     Atom selection)		/* Selection to be cancelled. */
462 {
463     TkWindow *winPtr = (TkWindow *) tkwin;
464     TkDisplay *dispPtr = winPtr->dispPtr;
465     TkSelectionInfo *infoPtr;
466     TkSelectionInfo *prevPtr;
467     TkSelectionInfo *nextPtr;
468     Tk_LostSelProc *clearProc = NULL;
469     void *clearData = NULL;/* Initialization needed only to prevent
470 				 * compiler warning. */
471 
472     if (dispPtr->multipleAtom == None) {
473 	TkSelInit(tkwin);
474     }
475 
476     for (infoPtr = dispPtr->selectionInfoPtr, prevPtr = NULL;
477 	    infoPtr != NULL; infoPtr = nextPtr) {
478 	nextPtr = infoPtr->nextPtr;
479 	if (infoPtr->selection == selection) {
480 	    if (prevPtr == NULL) {
481 		dispPtr->selectionInfoPtr = nextPtr;
482 	    } else {
483 		prevPtr->nextPtr = nextPtr;
484 	    }
485 	    break;
486 	}
487 	prevPtr = infoPtr;
488     }
489 
490     if (infoPtr != NULL) {
491 	clearProc = infoPtr->clearProc;
492 	clearData = infoPtr->clearData;
493 	ckfree(infoPtr);
494     }
495     XSetSelectionOwner(winPtr->display, selection, None, CurrentTime);
496 
497     if (clearProc != NULL) {
498 	clearProc(clearData);
499     }
500 }
501 
502 /*
503  *--------------------------------------------------------------
504  *
505  * Tk_GetSelection --
506  *
507  *	Retrieve the value of a selection and pass it off (in pieces,
508  *	possibly) to a given function.
509  *
510  * Results:
511  *	The return value is a standard Tcl return value. If an error occurs
512  *	(such as no selection exists) then an error message is left in the
513  *	interp's result.
514  *
515  * Side effects:
516  *	The standard X11 protocols are used to retrieve the selection. When it
517  *	arrives, it is passed to proc. If the selection is very large, it will
518  *	be passed to proc in several pieces. Proc should have the following
519  *	structure:
520  *
521  *	int
522  *	proc(
523  *	    ClientData clientData,
524  *	    Tcl_Interp *interp,
525  *	    char *portion)
526  *	{
527  *	}
528  *
529  *	The interp and clientData arguments to proc will be the same as the
530  *	corresponding arguments to Tk_GetSelection. The portion argument
531  *	points to a character string containing part of the selection, and
532  *	numBytes indicates the length of the portion, not including the
533  *	terminating NULL character. If the selection arrives in several
534  *	pieces, the "portion" arguments in separate calls will contain
535  *	successive parts of the selection. Proc should normally return TCL_OK.
536  *	If it detects an error then it should return TCL_ERROR and leave an
537  *	error message in the interp's result; the remainder of the selection
538  *	retrieval will be aborted.
539  *
540  *--------------------------------------------------------------
541  */
542 
543 int
Tk_GetSelection(Tcl_Interp * interp,Tk_Window tkwin,Atom selection,Atom target,Tk_GetSelProc * proc,ClientData clientData)544 Tk_GetSelection(
545     Tcl_Interp *interp,		/* Interpreter to use for reporting errors. */
546     Tk_Window tkwin,		/* Window on whose behalf to retrieve the
547 				 * selection (determines display from which to
548 				 * retrieve). */
549     Atom selection,		/* Selection to retrieve. */
550     Atom target,		/* Desired form in which selection is to be
551 				 * returned. */
552     Tk_GetSelProc *proc,	/* Function to call to process the selection,
553 				 * once it has been retrieved. */
554     ClientData clientData)	/* Arbitrary value to pass to proc. */
555 {
556     TkWindow *winPtr = (TkWindow *) tkwin;
557     TkDisplay *dispPtr = winPtr->dispPtr;
558     TkSelectionInfo *infoPtr;
559     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
560 	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
561 
562     if (dispPtr->multipleAtom == None) {
563 	TkSelInit(tkwin);
564     }
565 
566     /*
567      * If the selection is owned by a window managed by this process, then
568      * call the retrieval function directly, rather than going through the X
569      * server (it's dangerous to go through the X server in this case because
570      * it could result in deadlock if an INCR-style selection results).
571      */
572 
573     for (infoPtr = dispPtr->selectionInfoPtr; infoPtr != NULL;
574 	    infoPtr = infoPtr->nextPtr) {
575 	if (infoPtr->selection == selection) {
576 	    break;
577 	}
578     }
579     if (infoPtr != NULL) {
580 	TkSelHandler *selPtr;
581 	int offset, result, count;
582 	char buffer[TK_SEL_BYTES_AT_ONCE+1];
583 	TkSelInProgress ip;
584 
585 	for (selPtr = ((TkWindow *) infoPtr->owner)->selHandlerList;
586 		selPtr != NULL; selPtr = selPtr->nextPtr) {
587 	    if (selPtr->target==target && selPtr->selection==selection) {
588 		break;
589 	    }
590 	}
591 	if (selPtr == NULL) {
592 	    Atom type;
593 
594 	    count = TkSelDefaultSelection(infoPtr, target, buffer,
595 		    TK_SEL_BYTES_AT_ONCE, &type);
596 	    if (count > TK_SEL_BYTES_AT_ONCE) {
597 		Tcl_Panic("selection handler returned too many bytes");
598 	    }
599 	    if (count < 0) {
600 		goto cantget;
601 	    }
602 	    buffer[count] = 0;
603 	    result = proc(clientData, interp, buffer);
604 	} else {
605 	    offset = 0;
606 	    result = TCL_OK;
607 	    ip.selPtr = selPtr;
608 	    ip.nextPtr = tsdPtr->pendingPtr;
609 	    tsdPtr->pendingPtr = &ip;
610 	    while (1) {
611 		count = selPtr->proc(selPtr->clientData, offset, buffer,
612 			TK_SEL_BYTES_AT_ONCE);
613 		if ((count < 0) || (ip.selPtr == NULL)) {
614 		    tsdPtr->pendingPtr = ip.nextPtr;
615 		    goto cantget;
616 		}
617 		if (count > TK_SEL_BYTES_AT_ONCE) {
618 		    Tcl_Panic("selection handler returned too many bytes");
619 		}
620 		buffer[count] = '\0';
621 		result = proc(clientData, interp, buffer);
622 		if ((result != TCL_OK) || (count < TK_SEL_BYTES_AT_ONCE)
623 			|| (ip.selPtr == NULL)) {
624 		    break;
625 		}
626 		offset += count;
627 	    }
628 	    tsdPtr->pendingPtr = ip.nextPtr;
629 	}
630 	return result;
631     }
632 
633     /*
634      * The selection is owned by some other process.
635      */
636 
637     return TkSelGetSelection(interp, tkwin, selection, target, proc,
638 	    clientData);
639 
640   cantget:
641     Tcl_SetObjResult(interp, Tcl_ObjPrintf(
642 	    "%s selection doesn't exist or form \"%s\" not defined",
643 	    Tk_GetAtomName(tkwin, selection),
644 	    Tk_GetAtomName(tkwin, target)));
645     return TCL_ERROR;
646 }
647 
648 /*
649  *--------------------------------------------------------------
650  *
651  * Tk_SelectionObjCmd --
652  *
653  *	This function is invoked to process the "selection" Tcl command. See
654  *	the user documentation for details on what it does.
655  *
656  * Results:
657  *	A standard Tcl result.
658  *
659  * Side effects:
660  *	See the user documentation.
661  *
662  *--------------------------------------------------------------
663  */
664 
665 int
Tk_SelectionObjCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])666 Tk_SelectionObjCmd(
667     ClientData clientData,	/* Main window associated with
668 				 * interpreter. */
669     Tcl_Interp *interp,		/* Current interpreter. */
670     int objc,			/* Number of arguments. */
671     Tcl_Obj *const objv[])	/* Argument objects. */
672 {
673     Tk_Window tkwin = (Tk_Window)clientData;
674     const char *path = NULL;
675     Atom selection;
676     const char *selName = NULL;
677     const char *string;
678     int count, index;
679     Tcl_Obj **objs;
680     static const char *const optionStrings[] = {
681 	"clear", "get", "handle", "own", NULL
682     };
683     enum options {
684 	SELECTION_CLEAR, SELECTION_GET, SELECTION_HANDLE, SELECTION_OWN
685     };
686 
687     if (objc < 2) {
688 	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
689 	return TCL_ERROR;
690     }
691 
692     if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
693 	    &index) != TCL_OK) {
694 	return TCL_ERROR;
695     }
696 
697     switch ((enum options) index) {
698     case SELECTION_CLEAR: {
699 	static const char *const clearOptionStrings[] = {
700 	    "-displayof", "-selection", NULL
701 	};
702 	enum clearOptions { CLEAR_DISPLAYOF, CLEAR_SELECTION };
703 	int clearIndex;
704 
705 	for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count > 0;
706 		count-=2, objs+=2) {
707 	    string = Tcl_GetString(objs[0]);
708 	    if (string[0] != '-') {
709 		break;
710 	    }
711 	    if (count < 2) {
712 		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
713 			"value for \"%s\" missing", string));
714 		Tcl_SetErrorCode(interp, "TK", "SELECTION", "VALUE", NULL);
715 		return TCL_ERROR;
716 	    }
717 
718 	    if (Tcl_GetIndexFromObj(interp, objs[0], clearOptionStrings,
719 		    "option", 0, &clearIndex) != TCL_OK) {
720 		return TCL_ERROR;
721 	    }
722 	    switch ((enum clearOptions) clearIndex) {
723 	    case CLEAR_DISPLAYOF:
724 		path = Tcl_GetString(objs[1]);
725 		break;
726 	    case CLEAR_SELECTION:
727 		selName = Tcl_GetString(objs[1]);
728 		break;
729 	    }
730 	}
731 
732 	if (count == 1) {
733 	    path = Tcl_GetString(objs[0]);
734 	} else if (count > 1) {
735 	    Tcl_WrongNumArgs(interp, 2, objv, "?-option value ...?");
736 	    return TCL_ERROR;
737 	}
738 	if (path != NULL) {
739 	    tkwin = Tk_NameToWindow(interp, path, tkwin);
740 	}
741 	if (tkwin == NULL) {
742 	    return TCL_ERROR;
743 	}
744 	if (selName != NULL) {
745 	    selection = Tk_InternAtom(tkwin, selName);
746 	} else {
747 	    selection = XA_PRIMARY;
748 	}
749 
750 	Tk_ClearSelection(tkwin, selection);
751 	break;
752     }
753 
754     case SELECTION_GET: {
755 	Atom target;
756 	const char *targetName = NULL;
757 	Tcl_DString selBytes;
758 	int result;
759 	static const char *const getOptionStrings[] = {
760 	    "-displayof", "-selection", "-type", NULL
761 	};
762 	enum getOptions { GET_DISPLAYOF, GET_SELECTION, GET_TYPE };
763 	int getIndex;
764 
765 	for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count>0;
766 		count-=2, objs+=2) {
767 	    string = Tcl_GetString(objs[0]);
768 	    if (string[0] != '-') {
769 		break;
770 	    }
771 	    if (count < 2) {
772 		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
773 			"value for \"%s\" missing", string));
774 		Tcl_SetErrorCode(interp, "TK", "SELECTION", "VALUE", NULL);
775 		return TCL_ERROR;
776 	    }
777 
778 	    if (Tcl_GetIndexFromObj(interp, objs[0], getOptionStrings,
779 		    "option", 0, &getIndex) != TCL_OK) {
780 		return TCL_ERROR;
781 	    }
782 
783 	    switch ((enum getOptions) getIndex) {
784 	    case GET_DISPLAYOF:
785 		path = Tcl_GetString(objs[1]);
786 		break;
787 	    case GET_SELECTION:
788 		selName = Tcl_GetString(objs[1]);
789 		break;
790 	    case GET_TYPE:
791 		targetName = Tcl_GetString(objs[1]);
792 		break;
793 	    }
794 	}
795 
796 	if (path != NULL) {
797 	    tkwin = Tk_NameToWindow(interp, path, tkwin);
798 	}
799 	if (tkwin == NULL) {
800 	    return TCL_ERROR;
801 	}
802 	if (selName != NULL) {
803 	    selection = Tk_InternAtom(tkwin, selName);
804 	} else {
805 	    selection = XA_PRIMARY;
806 	}
807 	if (count > 1) {
808 	    Tcl_WrongNumArgs(interp, 2, objv, "?-option value ...?");
809 	    return TCL_ERROR;
810 	} else if (count == 1) {
811 	    target = Tk_InternAtom(tkwin, Tcl_GetString(objs[0]));
812 	} else if (targetName != NULL) {
813 	    target = Tk_InternAtom(tkwin, targetName);
814 	} else {
815 	    target = XA_STRING;
816 	}
817 
818 	Tcl_DStringInit(&selBytes);
819 	result = Tk_GetSelection(interp, tkwin, selection, target,
820 		SelGetProc, &selBytes);
821 	if (result == TCL_OK) {
822 	    Tcl_DStringResult(interp, &selBytes);
823 	} else {
824 	    Tcl_DStringFree(&selBytes);
825 	}
826 	return result;
827     }
828 
829     case SELECTION_HANDLE: {
830 	Atom target, format;
831 	const char *targetName = NULL;
832 	const char *formatName = NULL;
833 	CommandInfo *cmdInfoPtr;
834 	TkSizeT cmdLength;
835 	static const char *const handleOptionStrings[] = {
836 	    "-format", "-selection", "-type", NULL
837 	};
838 	enum handleOptions {
839 	    HANDLE_FORMAT, HANDLE_SELECTION, HANDLE_TYPE
840 	};
841 	int handleIndex;
842 
843 	for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count > 0;
844 		count-=2, objs+=2) {
845 	    string = Tcl_GetString(objs[0]);
846 	    if (string[0] != '-') {
847 		break;
848 	    }
849 	    if (count < 2) {
850 		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
851 			"value for \"%s\" missing", string));
852 		Tcl_SetErrorCode(interp, "TK", "SELECTION", "VALUE", NULL);
853 		return TCL_ERROR;
854 	    }
855 
856 	    if (Tcl_GetIndexFromObj(interp, objs[0],handleOptionStrings,
857 		    "option", 0, &handleIndex) != TCL_OK) {
858 		return TCL_ERROR;
859 	    }
860 
861 	    switch ((enum handleOptions) handleIndex) {
862 	    case HANDLE_FORMAT:
863 		formatName = Tcl_GetString(objs[1]);
864 		break;
865 	    case HANDLE_SELECTION:
866 		selName = Tcl_GetString(objs[1]);
867 		break;
868 	    case HANDLE_TYPE:
869 		targetName = Tcl_GetString(objs[1]);
870 		break;
871 	    }
872 	}
873 
874 	if ((count < 2) || (count > 4)) {
875 	    Tcl_WrongNumArgs(interp, 2, objv,
876 		    "?-option value ...? window command");
877 	    return TCL_ERROR;
878 	}
879 	tkwin = Tk_NameToWindow(interp, Tcl_GetString(objs[0]), tkwin);
880 	if (tkwin == NULL) {
881 	    return TCL_ERROR;
882 	}
883 	if (selName != NULL) {
884 	    selection = Tk_InternAtom(tkwin, selName);
885 	} else {
886 	    selection = XA_PRIMARY;
887 	}
888 
889 	if (count > 2) {
890 	    target = Tk_InternAtom(tkwin, Tcl_GetString(objs[2]));
891 	} else if (targetName != NULL) {
892 	    target = Tk_InternAtom(tkwin, targetName);
893 	} else {
894 	    target = XA_STRING;
895 	}
896 	if (count > 3) {
897 	    format = Tk_InternAtom(tkwin, Tcl_GetString(objs[3]));
898 	} else if (formatName != NULL) {
899 	    format = Tk_InternAtom(tkwin, formatName);
900 	} else {
901 	    format = XA_STRING;
902 	}
903 	string = Tcl_GetStringFromObj(objs[1], &cmdLength);
904 	if (cmdLength == 0) {
905 	    Tk_DeleteSelHandler(tkwin, selection, target);
906 	} else {
907 	    cmdInfoPtr = (CommandInfo *)ckalloc(offsetof(CommandInfo, command)
908 		    + 1 + cmdLength);
909 	    cmdInfoPtr->interp = interp;
910 	    cmdInfoPtr->charOffset = 0;
911 	    cmdInfoPtr->byteOffset = 0;
912 	    cmdInfoPtr->buffer[0] = '\0';
913 	    cmdInfoPtr->cmdLength = cmdLength;
914 	    memcpy(cmdInfoPtr->command, string, cmdLength + 1);
915 	    Tk_CreateSelHandler(tkwin, selection, target, HandleTclCommand,
916 		    cmdInfoPtr, format);
917 	}
918 	return TCL_OK;
919     }
920 
921     case SELECTION_OWN: {
922 	LostCommand *lostPtr;
923 	Tcl_Obj *commandObj = NULL;
924 	static const char *const ownOptionStrings[] = {
925 	    "-command", "-displayof", "-selection", NULL
926 	};
927 	enum ownOptions { OWN_COMMAND, OWN_DISPLAYOF, OWN_SELECTION };
928 	int ownIndex;
929 
930 	for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count > 0;
931 		count-=2, objs+=2) {
932 	    string = Tcl_GetString(objs[0]);
933 	    if (string[0] != '-') {
934 		break;
935 	    }
936 	    if (count < 2) {
937 		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
938 			"value for \"%s\" missing", string));
939 		Tcl_SetErrorCode(interp, "TK", "SELECTION", "VALUE", NULL);
940 		return TCL_ERROR;
941 	    }
942 
943 	    if (Tcl_GetIndexFromObj(interp, objs[0], ownOptionStrings,
944 		    "option", 0, &ownIndex) != TCL_OK) {
945 		return TCL_ERROR;
946 	    }
947 
948 	    switch ((enum ownOptions) ownIndex) {
949 	    case OWN_COMMAND:
950 		commandObj = objs[1];
951 		break;
952 	    case OWN_DISPLAYOF:
953 		path = Tcl_GetString(objs[1]);
954 		break;
955 	    case OWN_SELECTION:
956 		selName = Tcl_GetString(objs[1]);
957 		break;
958 	    }
959 	}
960 
961 	if (count > 2) {
962 	    Tcl_WrongNumArgs(interp, 2, objv, "?-option value ...? ?window?");
963 	    return TCL_ERROR;
964 	}
965 	if (selName != NULL) {
966 	    selection = Tk_InternAtom(tkwin, selName);
967 	} else {
968 	    selection = XA_PRIMARY;
969 	}
970 
971 	if (count == 0) {
972 	    TkSelectionInfo *infoPtr;
973 	    TkWindow *winPtr;
974 
975 	    if (path != NULL) {
976 		tkwin = Tk_NameToWindow(interp, path, tkwin);
977 	    }
978 	    if (tkwin == NULL) {
979 		return TCL_ERROR;
980 	    }
981 	    winPtr = (TkWindow *) tkwin;
982 	    for (infoPtr = winPtr->dispPtr->selectionInfoPtr;
983 		    infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
984 		if (infoPtr->selection == selection) {
985 		    break;
986 		}
987 	    }
988 
989 	    /*
990 	     * Ignore the internal clipboard window.
991 	     */
992 
993 	    if ((infoPtr != NULL)
994 		    && (infoPtr->owner != winPtr->dispPtr->clipWindow)) {
995 		Tcl_SetObjResult(interp, Tk_NewWindowObj(infoPtr->owner));
996 	    }
997 	    return TCL_OK;
998 	}
999 
1000 	tkwin = Tk_NameToWindow(interp, Tcl_GetString(objs[0]), tkwin);
1001 	if (tkwin == NULL) {
1002 	    return TCL_ERROR;
1003 	}
1004 	if (count == 2) {
1005 	    commandObj = objs[1];
1006 	}
1007 	if (commandObj == NULL) {
1008 	    Tk_OwnSelection(tkwin, selection, NULL, NULL);
1009 	    return TCL_OK;
1010 	}
1011 	lostPtr = (LostCommand *)ckalloc(sizeof(LostCommand));
1012 	lostPtr->interp = interp;
1013 	lostPtr->cmdObj = commandObj;
1014 	Tcl_IncrRefCount(commandObj);
1015 	Tk_OwnSelection(tkwin, selection, LostSelection, lostPtr);
1016 	return TCL_OK;
1017     }
1018     }
1019     return TCL_OK;
1020 }
1021 
1022 /*
1023  *----------------------------------------------------------------------
1024  *
1025  * TkSelGetInProgress --
1026  *
1027  *	This function returns a pointer to the thread-local list of pending
1028  *	searches.
1029  *
1030  * Results:
1031  *	The return value is a pointer to the first search in progress, or NULL
1032  *	if there are none.
1033  *
1034  * Side effects:
1035  *	None.
1036  *
1037  *----------------------------------------------------------------------
1038  */
1039 
1040 TkSelInProgress *
TkSelGetInProgress(void)1041 TkSelGetInProgress(void)
1042 {
1043     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1044 	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1045 
1046     return tsdPtr->pendingPtr;
1047 }
1048 
1049 /*
1050  *----------------------------------------------------------------------
1051  *
1052  * TkSelSetInProgress --
1053  *
1054  *	This function is used to set the thread-local list of pending
1055  *	searches. It is required because the pending list is kept in thread
1056  *	local storage.
1057  *
1058  * Results:
1059  *	None.
1060  *
1061  * Side effects:
1062  *	None.
1063  *
1064  *----------------------------------------------------------------------
1065  */
1066 void
TkSelSetInProgress(TkSelInProgress * pendingPtr)1067 TkSelSetInProgress(
1068     TkSelInProgress *pendingPtr)
1069 {
1070     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1071 	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1072 
1073     tsdPtr->pendingPtr = pendingPtr;
1074 }
1075 
1076 /*
1077  *----------------------------------------------------------------------
1078  *
1079  * TkSelDeadWindow --
1080  *
1081  *	This function is invoked just before a TkWindow is deleted. It
1082  *	performs selection-related cleanup.
1083  *
1084  * Results:
1085  *	None.
1086  *
1087  * Side effects:
1088  *	Frees up memory associated with the selection.
1089  *
1090  *----------------------------------------------------------------------
1091  */
1092 
1093 void
TkSelDeadWindow(TkWindow * winPtr)1094 TkSelDeadWindow(
1095     TkWindow *winPtr)	/* Window that's being deleted. */
1096 {
1097     TkSelHandler *selPtr;
1098     TkSelInProgress *ipPtr;
1099     TkSelectionInfo *infoPtr, *prevPtr, *nextPtr;
1100     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1101 	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1102 
1103     /*
1104      * While deleting all the handlers, be careful to check whether
1105      * ConvertSelection or TkSelPropProc are about to process one of the
1106      * deleted handlers.
1107      */
1108 
1109     while (winPtr->selHandlerList != NULL) {
1110 	selPtr = winPtr->selHandlerList;
1111 	winPtr->selHandlerList = selPtr->nextPtr;
1112 	for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL;
1113 		ipPtr = ipPtr->nextPtr) {
1114 	    if (ipPtr->selPtr == selPtr) {
1115 		ipPtr->selPtr = NULL;
1116 	    }
1117 	}
1118 	if (selPtr->proc == HandleTclCommand) {
1119 	    /*
1120 	     * Mark the CommandInfo as deleted and free it when we can.
1121 	     */
1122 
1123 	    ((CommandInfo *) selPtr->clientData)->interp = NULL;
1124 	    Tcl_EventuallyFree(selPtr->clientData, TCL_DYNAMIC);
1125 	}
1126 	ckfree(selPtr);
1127     }
1128 
1129     /*
1130      * Remove selections owned by window being deleted.
1131      */
1132 
1133     for (infoPtr = winPtr->dispPtr->selectionInfoPtr, prevPtr = NULL;
1134 	    infoPtr != NULL; infoPtr = nextPtr) {
1135 	nextPtr = infoPtr->nextPtr;
1136 	if (infoPtr->owner == (Tk_Window) winPtr) {
1137 	    if (infoPtr->clearProc == LostSelection) {
1138 		ckfree(infoPtr->clearData);
1139 	    }
1140 	    ckfree(infoPtr);
1141 	    infoPtr = prevPtr;
1142 	    if (prevPtr == NULL) {
1143 		winPtr->dispPtr->selectionInfoPtr = nextPtr;
1144 	    } else {
1145 		prevPtr->nextPtr = nextPtr;
1146 	    }
1147 	}
1148 	prevPtr = infoPtr;
1149     }
1150 }
1151 
1152 /*
1153  *----------------------------------------------------------------------
1154  *
1155  * TkSelInit --
1156  *
1157  *	Initialize selection-related information for a display.
1158  *
1159  * Results:
1160  *	None.
1161  *
1162  * Side effects:
1163  *	Selection-related information is initialized.
1164  *
1165  *----------------------------------------------------------------------
1166  */
1167 
1168 void
TkSelInit(Tk_Window tkwin)1169 TkSelInit(
1170     Tk_Window tkwin)		/* Window token (used to find display to
1171 				 * initialize). */
1172 {
1173     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
1174 
1175     /*
1176      * Fetch commonly-used atoms.
1177      */
1178 
1179     dispPtr->multipleAtom	= Tk_InternAtom(tkwin, "MULTIPLE");
1180     dispPtr->incrAtom		= Tk_InternAtom(tkwin, "INCR");
1181     dispPtr->targetsAtom	= Tk_InternAtom(tkwin, "TARGETS");
1182     dispPtr->timestampAtom	= Tk_InternAtom(tkwin, "TIMESTAMP");
1183     dispPtr->textAtom		= Tk_InternAtom(tkwin, "TEXT");
1184     dispPtr->compoundTextAtom	= Tk_InternAtom(tkwin, "COMPOUND_TEXT");
1185     dispPtr->applicationAtom	= Tk_InternAtom(tkwin, "TK_APPLICATION");
1186     dispPtr->windowAtom		= Tk_InternAtom(tkwin, "TK_WINDOW");
1187     dispPtr->clipboardAtom	= Tk_InternAtom(tkwin, "CLIPBOARD");
1188     dispPtr->atomPairAtom	= Tk_InternAtom(tkwin, "ATOM_PAIR");
1189 
1190     /*
1191      * Using UTF8_STRING instead of the XA_UTF8_STRING macro allows us to
1192      * support older X servers that didn't have UTF8_STRING yet. This is
1193      * necessary on Unix systems. For more information, see:
1194      *	  http://www.cl.cam.ac.uk/~mgk25/unicode.html#x11
1195      */
1196 
1197 #if !defined(_WIN32)
1198     dispPtr->utf8Atom		= Tk_InternAtom(tkwin, "UTF8_STRING");
1199 #else
1200     dispPtr->utf8Atom		= (Atom) 0;
1201 #endif
1202 }
1203 
1204 /*
1205  *----------------------------------------------------------------------
1206  *
1207  * TkSelClearSelection --
1208  *
1209  *	This function is invoked to process a SelectionClear event.
1210  *
1211  * Results:
1212  *	None.
1213  *
1214  * Side effects:
1215  *	Invokes the clear function for the window which lost the
1216  *	selection.
1217  *
1218  *----------------------------------------------------------------------
1219  */
1220 
1221 void
TkSelClearSelection(Tk_Window tkwin,XEvent * eventPtr)1222 TkSelClearSelection(
1223     Tk_Window tkwin,		/* Window for which event was targeted. */
1224     XEvent *eventPtr)	/* X SelectionClear event. */
1225 {
1226     TkWindow *winPtr = (TkWindow *) tkwin;
1227     TkDisplay *dispPtr = winPtr->dispPtr;
1228     TkSelectionInfo *infoPtr;
1229     TkSelectionInfo *prevPtr;
1230 
1231     /*
1232      * Invoke clear function for window that just lost the selection. This
1233      * code is a bit tricky, because any callbacks due to selection changes
1234      * between windows managed by the process have already been made. Thus,
1235      * ignore the event unless it refers to the window that's currently the
1236      * selection owner and the event was generated after the server saw the
1237      * SetSelectionOwner request.
1238      */
1239 
1240     for (infoPtr = dispPtr->selectionInfoPtr, prevPtr = NULL;
1241 	    infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
1242 	if (infoPtr->selection == eventPtr->xselectionclear.selection) {
1243 	    break;
1244 	}
1245 	prevPtr = infoPtr;
1246     }
1247 
1248     if (infoPtr != NULL && (infoPtr->owner == tkwin) &&
1249 	    (eventPtr->xselectionclear.serial >= (unsigned long) infoPtr->serial)) {
1250 	if (prevPtr == NULL) {
1251 	    dispPtr->selectionInfoPtr = infoPtr->nextPtr;
1252 	} else {
1253 	    prevPtr->nextPtr = infoPtr->nextPtr;
1254 	}
1255 
1256 	/*
1257 	 * Because of reentrancy problems, calling clearProc must be done
1258 	 * after the infoPtr has been removed from the selectionInfoPtr list
1259 	 * (clearProc could modify the list, e.g. by creating a new
1260 	 * selection).
1261 	 */
1262 
1263 	if (infoPtr->clearProc != NULL) {
1264 	    infoPtr->clearProc(infoPtr->clearData);
1265 	}
1266 	ckfree(infoPtr);
1267     }
1268 }
1269 
1270 /*
1271  *--------------------------------------------------------------
1272  *
1273  * SelGetProc --
1274  *
1275  *	This function is invoked to process pieces of the selection as they
1276  *	arrive during "selection get" commands.
1277  *
1278  * Results:
1279  *	Always returns TCL_OK.
1280  *
1281  * Side effects:
1282  *	Bytes get appended to the dynamic string pointed to by the clientData
1283  *	argument.
1284  *
1285  *--------------------------------------------------------------
1286  */
1287 
1288 static int
SelGetProc(ClientData clientData,TCL_UNUSED (Tcl_Interp *),const char * portion)1289 SelGetProc(
1290     ClientData clientData,	/* Dynamic string holding partially assembled
1291 				 * selection. */
1292     TCL_UNUSED(Tcl_Interp *),	/* Interpreter used for error reporting (not
1293 				 * used). */
1294     const char *portion)	/* New information to be appended. */
1295 {
1296     Tcl_DStringAppend((Tcl_DString *)clientData, portion, -1);
1297     return TCL_OK;
1298 }
1299 
1300 /*
1301  *----------------------------------------------------------------------
1302  *
1303  * HandleTclCommand --
1304  *
1305  *	This function acts as selection handler for handlers created by the
1306  *	"selection handle" command. It invokes a Tcl command to retrieve the
1307  *	selection.
1308  *
1309  * Results:
1310  *	The return value is a count of the number of bytes actually stored at
1311  *	buffer, or -1 if an error occurs while executing the Tcl command to
1312  *	retrieve the selection.
1313  *
1314  * Side effects:
1315  *	None except for things done by the Tcl command.
1316  *
1317  *----------------------------------------------------------------------
1318  */
1319 
1320 static TkSizeT
HandleTclCommand(ClientData clientData,TkSizeT offset,char * buffer,TkSizeT maxBytes)1321 HandleTclCommand(
1322     ClientData clientData,	/* Information about command to execute. */
1323     TkSizeT offset,			/* Return selection bytes starting at this
1324 				 * offset. */
1325     char *buffer,		/* Place to store converted selection. */
1326     TkSizeT maxBytes)		/* Maximum # of bytes to store at buffer. */
1327 {
1328     CommandInfo *cmdInfoPtr = (CommandInfo *)clientData;
1329     int length;
1330     Tcl_Obj *command;
1331     const char *string;
1332     Tcl_Interp *interp = cmdInfoPtr->interp;
1333     Tcl_InterpState savedState;
1334     int extraBytes, charOffset, count, numChars, code;
1335     const char *p;
1336 
1337     /*
1338      * We must also protect the interpreter and the command from being deleted
1339      * too soon.
1340      */
1341 
1342     Tcl_Preserve(clientData);
1343     Tcl_Preserve(interp);
1344 
1345     /*
1346      * Compute the proper byte offset in the case where the last chunk split a
1347      * character.
1348      */
1349 
1350     if ((int)offset == cmdInfoPtr->byteOffset) {
1351 	charOffset = cmdInfoPtr->charOffset;
1352 	extraBytes = strlen(cmdInfoPtr->buffer);
1353 	if (extraBytes > 0) {
1354 	    strcpy(buffer, cmdInfoPtr->buffer);
1355 	    maxBytes -= extraBytes;
1356 	    buffer += extraBytes;
1357 	}
1358     } else {
1359 	cmdInfoPtr->byteOffset = 0;
1360 	cmdInfoPtr->charOffset = 0;
1361 	extraBytes = 0;
1362 	charOffset = 0;
1363     }
1364 
1365     /*
1366      * First, generate a command by taking the command string and appending
1367      * the offset and maximum # of bytes.
1368      */
1369 
1370     command = Tcl_ObjPrintf("%s %d %d",
1371 	    cmdInfoPtr->command, charOffset, (int)maxBytes);
1372     Tcl_IncrRefCount(command);
1373 
1374     /*
1375      * Execute the command. Be sure to restore the state of the interpreter
1376      * after executing the command.
1377      */
1378 
1379     savedState = Tcl_SaveInterpState(interp, TCL_OK);
1380     code = Tcl_EvalObjEx(interp, command, TCL_EVAL_GLOBAL);
1381     Tcl_DecrRefCount(command);
1382     if (code == TCL_OK) {
1383 	/*
1384 	 * TODO: This assumes that bytes are characters; that's not true!
1385 	 */
1386 
1387 	string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &length);
1388 	count = (length > (int)maxBytes) ? (int)maxBytes : length;
1389 	memcpy(buffer, string, count);
1390 	buffer[count] = '\0';
1391 
1392 	/*
1393 	 * Update the partial character information for the next retrieval if
1394 	 * the command has not been deleted.
1395 	 */
1396 
1397 	if (cmdInfoPtr->interp != NULL) {
1398 	    if (length <= (int)maxBytes) {
1399 		cmdInfoPtr->charOffset += Tcl_NumUtfChars(string, -1);
1400 		cmdInfoPtr->buffer[0] = '\0';
1401 	    } else {
1402 		Tcl_UniChar ch = 0;
1403 		p = string;
1404 		string += count;
1405 		numChars = 0;
1406 		while (p < string) {
1407 		    p += Tcl_UtfToUniChar(p, &ch);
1408 		    numChars++;
1409 		}
1410 		cmdInfoPtr->charOffset += numChars;
1411 		length = p - string;
1412 		if (length > 0) {
1413 		    strncpy(cmdInfoPtr->buffer, string, length);
1414 		}
1415 		cmdInfoPtr->buffer[length] = '\0';
1416 	    }
1417 	    cmdInfoPtr->byteOffset += count + extraBytes;
1418 	}
1419 	count += extraBytes;
1420     } else {
1421 	/*
1422 	 * Something went wrong. Log errors as background errors, and silently
1423 	 * drop everything else.
1424 	 */
1425 
1426 	if (code == TCL_ERROR) {
1427 	    Tcl_AddErrorInfo(interp, "\n    (command handling selection)");
1428 	    Tcl_BackgroundException(interp, code);
1429 	}
1430 	count = -1;
1431     }
1432     (void) Tcl_RestoreInterpState(interp, savedState);
1433 
1434     Tcl_Release(clientData);
1435     Tcl_Release(interp);
1436     return count;
1437 }
1438 
1439 /*
1440  *----------------------------------------------------------------------
1441  *
1442  * TkSelDefaultSelection --
1443  *
1444  *	This function is called to generate selection information for a few
1445  *	standard targets such as TIMESTAMP and TARGETS. It is invoked only if
1446  *	no handler has been declared by the application.
1447  *
1448  * Results:
1449  *	If "target" is a standard target understood by this function, the
1450  *	selection is converted to that form and stored as a character string
1451  *	in buffer. The type of the selection (e.g. STRING or ATOM) is stored
1452  *	in *typePtr, and the return value is a count of the # of non-NULL
1453  *	bytes at buffer. If the target wasn't understood, or if there isn't
1454  *	enough space at buffer to hold the entire selection (no INCR-mode
1455  *	transfers for this stuff!), then -1 is returned.
1456  *
1457  * Side effects:
1458  *	None.
1459  *
1460  *----------------------------------------------------------------------
1461  */
1462 
1463 int
TkSelDefaultSelection(TkSelectionInfo * infoPtr,Atom target,char * buffer,int maxBytes,Atom * typePtr)1464 TkSelDefaultSelection(
1465     TkSelectionInfo *infoPtr,	/* Info about selection being retrieved. */
1466     Atom target,		/* Desired form of selection. */
1467     char *buffer,		/* Place to put selection characters. */
1468     int maxBytes,		/* Maximum # of bytes to store at buffer. */
1469     Atom *typePtr)		/* Store here the type of the selection, for
1470 				 * use in converting to proper X format. */
1471 {
1472     TkWindow *winPtr = (TkWindow *) infoPtr->owner;
1473     TkDisplay *dispPtr = winPtr->dispPtr;
1474 
1475     if (target == dispPtr->timestampAtom) {
1476 	if (maxBytes < 20) {
1477 	    return -1;
1478 	}
1479 	sprintf(buffer, "0x%x", (unsigned int) infoPtr->time);
1480 	*typePtr = XA_INTEGER;
1481 	return strlen(buffer);
1482     }
1483 
1484     if (target == dispPtr->targetsAtom) {
1485 	TkSelHandler *selPtr;
1486 	int length;
1487 	Tcl_DString ds;
1488 
1489 	if (maxBytes < 50) {
1490 	    return -1;
1491 	}
1492 	Tcl_DStringInit(&ds);
1493 	Tcl_DStringAppend(&ds,
1494 		"MULTIPLE TARGETS TIMESTAMP TK_APPLICATION TK_WINDOW", -1);
1495 	for (selPtr = winPtr->selHandlerList; selPtr != NULL;
1496 		selPtr = selPtr->nextPtr) {
1497 	    if ((selPtr->selection == infoPtr->selection)
1498 		    && (selPtr->target != dispPtr->applicationAtom)
1499 		    && (selPtr->target != dispPtr->windowAtom)) {
1500 		const char *atomString = Tk_GetAtomName((Tk_Window) winPtr,
1501 			selPtr->target);
1502 
1503 		Tcl_DStringAppendElement(&ds, atomString);
1504 	    }
1505 	}
1506 	length = Tcl_DStringLength(&ds);
1507 	if (length >= maxBytes) {
1508 	    Tcl_DStringFree(&ds);
1509 	    return -1;
1510 	}
1511 	memcpy(buffer, Tcl_DStringValue(&ds), length + 1);
1512 	Tcl_DStringFree(&ds);
1513 	*typePtr = XA_ATOM;
1514 	return length;
1515     }
1516 
1517     if (target == dispPtr->applicationAtom) {
1518 	int length;
1519 	Tk_Uid name = winPtr->mainPtr->winPtr->nameUid;
1520 
1521 	length = strlen(name);
1522 	if (maxBytes <= length) {
1523 	    return -1;
1524 	}
1525 	strcpy(buffer, name);
1526 	*typePtr = XA_STRING;
1527 	return length;
1528     }
1529 
1530     if (target == dispPtr->windowAtom) {
1531 	int length;
1532 	char *name = winPtr->pathName;
1533 
1534 	length = strlen(name);
1535 	if (maxBytes <= length) {
1536 	    return -1;
1537 	}
1538 	strcpy(buffer, name);
1539 	*typePtr = XA_STRING;
1540 	return length;
1541     }
1542 
1543     return -1;
1544 }
1545 
1546 /*
1547  *----------------------------------------------------------------------
1548  *
1549  * LostSelection --
1550  *
1551  *	This function is invoked when a window has lost ownership of the
1552  *	selection and the ownership was claimed with the command "selection
1553  *	own".
1554  *
1555  * Results:
1556  *	None.
1557  *
1558  * Side effects:
1559  *	A Tcl script is executed; it can do almost anything.
1560  *
1561  *----------------------------------------------------------------------
1562  */
1563 
1564 static void
LostSelection(ClientData clientData)1565 LostSelection(
1566     ClientData clientData)	/* Pointer to LostCommand structure. */
1567 {
1568     LostCommand *lostPtr = (LostCommand *)clientData;
1569     Tcl_Interp *interp = lostPtr->interp;
1570     Tcl_InterpState savedState;
1571     int code;
1572 
1573     Tcl_Preserve(interp);
1574 
1575     /*
1576      * Execute the command. Save the interpreter's result, if any, and restore
1577      * it after executing the command.
1578      */
1579 
1580     savedState = Tcl_SaveInterpState(interp, TCL_OK);
1581     Tcl_ResetResult(interp);
1582     code = Tcl_EvalObjEx(interp, lostPtr->cmdObj, TCL_EVAL_GLOBAL);
1583     if (code != TCL_OK) {
1584 	Tcl_BackgroundException(interp, code);
1585     }
1586     (void) Tcl_RestoreInterpState(interp, savedState);
1587 
1588     /*
1589      * Free the storage for the command, since we're done with it now.
1590      */
1591 
1592     Tcl_DecrRefCount(lostPtr->cmdObj);
1593     ckfree(lostPtr);
1594     Tcl_Release(interp);
1595 }
1596 
1597 /*
1598  * Local Variables:
1599  * mode: c
1600  * c-basic-offset: 4
1601  * fill-column: 78
1602  * End:
1603  */
1604