From d8d6f63a208246d2f1b9366a3c009b0438e84e28 Mon Sep 17 00:00:00 2001
From: dkf <donal.k.fellows@manchester.ac.uk>
Date: Tue, 22 Nov 2005 11:32:35 +0000
Subject: Backport of fixes for [Bug 1353414]

---
 ChangeLog           |   4 ++
 generic/tkSelect.c  |  27 +++++----
 tests/select.test   |  17 +++++-
 unix/tkUnixSelect.c | 166 +++++++++++++++++++++-------------------------------
 4 files changed, 103 insertions(+), 111 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 9f127e8..b8032fa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2005-11-22  Donal K. Fellows  <donal.k.fellows@man.ac.uk>
 
+	* unix/tkUnixSelect.c (SelCvtToX, SelCvtFromX): Backport of fixes for
+	* generic/tkSelect.c (TkSelDefaultSelection):	"spaces in atom names"
+	* tests/select.test (select-9.5):		problems. [Bug 1353414]
+
 	* library/tkfbox.tcl (::tk::dialog::file::): Correct the quoting of
 	the script used in variable traces so that widget names with spaces in
 	will work. [Bug 1335485]
diff --git a/generic/tkSelect.c b/generic/tkSelect.c
index d62aa43..73e41d8 100644
--- a/generic/tkSelect.c
+++ b/generic/tkSelect.c
@@ -11,7 +11,7 @@
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  *
- * RCS: @(#) $Id: tkSelect.c,v 1.13 2003/01/14 19:24:56 jenglish Exp $
+ * RCS: @(#) $Id: tkSelect.c,v 1.13.2.1 2005/11/22 11:32:37 dkf Exp $
  */
 
 #include "tkInt.h"
@@ -1489,29 +1489,32 @@ TkSelDefaultSelection(infoPtr, target, buffer, maxBytes, typePtr)
 
     if (target == dispPtr->targetsAtom) {
 	register TkSelHandler *selPtr;
-	CONST char *atomString;
-	int length, atomLength;
+	int length;
+	Tcl_DString ds;
 
 	if (maxBytes < 50) {
 	    return -1;
 	}
-	strcpy(buffer, "MULTIPLE TARGETS TIMESTAMP TK_APPLICATION TK_WINDOW");
-	length = strlen(buffer);
+	Tcl_DStringInit(&ds);
+	Tcl_DStringAppend(&ds,
+		"MULTIPLE TARGETS TIMESTAMP TK_APPLICATION TK_WINDOW", -1);
 	for (selPtr = winPtr->selHandlerList; selPtr != NULL;
 		selPtr = selPtr->nextPtr) {
 	    if ((selPtr->selection == infoPtr->selection)
 		    && (selPtr->target != dispPtr->applicationAtom)
 		    && (selPtr->target != dispPtr->windowAtom)) {
-		atomString = Tk_GetAtomName((Tk_Window) winPtr,
+		CONST char *atomString = Tk_GetAtomName((Tk_Window) winPtr,
 			selPtr->target);
-		atomLength = strlen(atomString) + 1;
-		if ((length + atomLength) >= maxBytes) {
-		    return -1;
-		}
-		sprintf(buffer+length, " %s", atomString);
-		length += atomLength;
+		Tcl_DStringAppendElement(&ds, atomString);
 	    }
 	}
+	length = Tcl_DStringLength(&ds);
+	if (length >= maxBytes) {
+	    Tcl_DStringFree(&ds);
+	    return -1;
+	}
+	memcpy(buffer, Tcl_DStringValue(&ds), (unsigned) (1+length));
+	Tcl_DStringFree(&ds);
 	*typePtr = XA_ATOM;
 	return length;
     }
diff --git a/tests/select.test b/tests/select.test
index 0e24c7c..4e63f9b 100644
--- a/tests/select.test
+++ b/tests/select.test
@@ -6,7 +6,7 @@
 # Copyright (c) 1998-1999 by Scriptics Corporation.
 # All rights reserved.
 #
-# RCS: @(#) $Id: select.test,v 1.9 2002/07/13 20:28:35 dgp Exp $
+# RCS: @(#) $Id: select.test,v 1.9.2.1 2005/11/22 11:32:37 dkf Exp $
 
 #
 # Note: Multiple display selection handling will only be tested if the
@@ -848,6 +848,21 @@ test select-9.4 {SelCvtToX and SelCvtFromX procedures} {unixOnly} {
     cleanupbg
     lappend result $selInfo
 } {{0x10 0x0 0x20} {TEST 0 4000}}
+test select-9.5 {SelCvtToX and SelCvtFromX procedures} -setup {
+    setup
+    setupbg
+} -constraints unix -body {
+    # Ensure that lists of atoms are constructed correctly, even when the
+    # atom names have spaces in. [Bug 1353414]
+    set selValue "foo bar"
+    set selInfo ""
+    set selType {text/x-tk-test;detail="foo bar"}
+    selection handle -selection PRIMARY -format STRING -type $selType \
+	.f1 [list handler $selType]
+    lsort [dobg {selection get TARGETS}]
+} -cleanup {
+    cleanupbg
+} -result {MULTIPLE TARGETS TIMESTAMP TK_APPLICATION TK_WINDOW {text/x-tk-test;detail="foo bar"}}
 
 ##############################################################################
 
diff --git a/unix/tkUnixSelect.c b/unix/tkUnixSelect.c
index 92add7f..2592d33 100644
--- a/unix/tkUnixSelect.c
+++ b/unix/tkUnixSelect.c
@@ -9,7 +9,7 @@
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  *
- * RCS: @(#) $Id: tkUnixSelect.c,v 1.11 2002/10/01 08:48:08 dkf Exp $
+ * RCS: @(#) $Id: tkUnixSelect.c,v 1.11.2.1 2005/11/22 11:32:37 dkf Exp $
  */
 
 #include "tkInt.h"
@@ -99,8 +99,8 @@ static TkSelRetrievalInfo *pendingRetrievals = NULL;
 static void		ConvertSelection _ANSI_ARGS_((TkWindow *winPtr,
 			    XSelectionRequestEvent *eventPtr));
 static void		IncrTimeoutProc _ANSI_ARGS_((ClientData clientData));
-static char *		SelCvtFromX _ANSI_ARGS_((long *propPtr, int numValues,
-			    Atom type, Tk_Window tkwin));
+static void		SelCvtFromX _ANSI_ARGS_((long *propPtr, int numValues,
+			    Atom type, Tk_Window tkwin, Tcl_DString *dsPtr));
 static long *		SelCvtToX _ANSI_ARGS_((char *string, Atom type,
 			    Tk_Window tkwin, int *numLongsPtr));
 static int		SelectionSize _ANSI_ARGS_((TkSelHandler *selPtr));
@@ -461,11 +461,16 @@ TkSelPropProc(eventPtr)
 			formatType, (Tk_Window) incrPtr->winPtr,
 			&numItems);
 
+		if (propPtr == NULL) {
+		    numItems = 0;
+		}
 		XChangeProperty(eventPtr->xproperty.display,
 			eventPtr->xproperty.window, eventPtr->xproperty.atom,
 			formatType, 32, PropModeReplace,
 			(unsigned char *) propPtr, numItems);
-		ckfree(propPtr);
+		if (propPtr != NULL) {
+		    ckfree(propPtr);
+		}
 	    }
 	    Tk_DeleteErrorHandler(errorHandler);
 
@@ -676,7 +681,7 @@ TkSelEventProc(tkwin, eventPtr)
 	    Tk_DeleteEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
 		    (ClientData) retrPtr);
 	} else {
-	    char *string;
+	    Tcl_DString ds;
 
 	    if (format != 32) {
 		char buf[64 + TCL_INTEGER_SPACE];
@@ -688,14 +693,15 @@ TkSelEventProc(tkwin, eventPtr)
 		retrPtr->result = TCL_ERROR;
 		return;
 	    }
-	    string = SelCvtFromX((long *) propInfo, (int) numItems, type,
-		    (Tk_Window) winPtr);
+	    Tcl_DStringInit(&ds);
+	    SelCvtFromX((long *) propInfo, (int) numItems, type,
+		    (Tk_Window) winPtr, &ds);
             interp = retrPtr->interp;
             Tcl_Preserve((ClientData) interp);
 	    retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
-		    interp, string);
+		    interp, Tcl_DStringValue(&ds));
             Tcl_Release((ClientData) interp);
-	    ckfree(string);
+	    Tcl_DStringFree(&ds);
 	}
 	XFree(propInfo);
 	return;
@@ -1000,6 +1006,9 @@ ConvertSelection(winPtr, eventPtr)
 	} else {
 	    propPtr = (char *) SelCvtToX((char *) buffer,
 		    type, (Tk_Window) winPtr, &numItems);
+	    if (propPtr == NULL) {
+		goto refuse;
+	    }
 	    format = 32;
 	    XChangeProperty(reply.display, reply.requestor,
 		    property, type, format, PropModeReplace,
@@ -1260,7 +1269,7 @@ SelRcvIncrProc(clientData, eventPtr)
     } else if (numItems == 0) {
 	retrPtr->result = TCL_OK;
     } else {
-	char *string;
+	Tcl_DString ds;
 
 	if (format != 32) {
 	    char buf[64 + TCL_INTEGER_SPACE];
@@ -1272,19 +1281,21 @@ SelRcvIncrProc(clientData, eventPtr)
 	    retrPtr->result = TCL_ERROR;
 	    goto done;
 	}
-	string = SelCvtFromX((long *) propInfo, (int) numItems, type,
-		(Tk_Window) retrPtr->winPtr);
+	Tcl_DStringInit(&ds);
+	SelCvtFromX((long *) propInfo, (int) numItems, type,
+		(Tk_Window) retrPtr->winPtr, &ds);
         interp = retrPtr->interp;
         Tcl_Preserve((ClientData) interp);
-	result = (*retrPtr->proc)(retrPtr->clientData, interp, string);
+	result = (*retrPtr->proc)(retrPtr->clientData, interp,
+		Tcl_DStringValue(&ds));
         Tcl_Release((ClientData) interp);
+	Tcl_DStringFree(&ds);
 	if (result != TCL_OK) {
 	    retrPtr->result = result;
 	}
-	ckfree(string);
     }
 
-    done:
+  done:
     XFree(propInfo);
     retrPtr->idleTime = 0;
 }
@@ -1405,28 +1416,21 @@ SelCvtToX(string, type, tkwin, numLongsPtr)
     int *numLongsPtr;		/* Number of 32-bit words contained in the
 				 * result. */
 {
-    register char *p;
-    char *field;
-    int numFields;
-    long *propPtr, *longPtr;
-#define MAX_ATOM_NAME_LENGTH 100
-    char atomName[MAX_ATOM_NAME_LENGTH+1];
+    const char **field;
+    int numFields, i;
+    long *propPtr;
 
     /*
-     * The string is assumed to consist of fields separated by spaces.
-     * The property gets generated by converting each field to an
-     * integer number, in one of two ways:
-     * 1. If type is XA_ATOM, convert each field to its corresponding
-     *	  atom.
-     * 2. If type is anything else, convert each field from an ASCII number
-     *    to a 32-bit binary number.
+     * The string is assumed to consist of fields separated by spaces. The
+     * property gets generated by converting each field to an integer number,
+     * in one of two ways:
+     * 1. If type is XA_ATOM, convert each field to its corresponding atom.
+     * 2. If type is anything else, convert each field from an ASCII number to
+     *    a 32-bit binary number.
      */
 
-    numFields = 1;
-    for (p = string; *p != 0; p++) {
-	if (isspace(UCHAR(*p))) {
-	    numFields++;
-	}
+    if (Tcl_SplitList(NULL, string, &numFields, &field) != TCL_OK) {
+	return NULL;
     }
     propPtr = (long *) ckalloc((unsigned) numFields*sizeof(long));
 
@@ -1434,34 +1438,27 @@ SelCvtToX(string, type, tkwin, numLongsPtr)
      * Convert the fields one-by-one.
      */
 
-    for (longPtr = propPtr, *numLongsPtr = 0, p = string;
-	    ; longPtr++, (*numLongsPtr)++) {
-	while (isspace(UCHAR(*p))) {
-	    p++;
-	}
-	if (*p == 0) {
-	    break;
-	}
-	field = p;
-	while ((*p != 0) && !isspace(UCHAR(*p))) {
-	    p++;
-	}
+    for (i=0 ; i<numFields ; i++) {
 	if (type == XA_ATOM) {
-	    int length;
-
-	    length = p - field;
-	    if (length > MAX_ATOM_NAME_LENGTH) {
-		length = MAX_ATOM_NAME_LENGTH;
-	    }
-	    strncpy(atomName, field, (unsigned) length);
-	    atomName[length] = 0;
-	    *longPtr = (long) Tk_InternAtom(tkwin, atomName);
+	    propPtr[i] = (long) Tk_InternAtom(tkwin, field[i]);
 	} else {
 	    char *dummy;
 
-	    *longPtr = strtol(field, &dummy, 0);
+	    /*
+	     * If this fails to parse a number, we just plunge on regardless
+	     * anyway.
+	     */
+
+	    propPtr[i] = strtol(field[i], &dummy, 0);
 	}
     }
+
+    /*
+     * Release the parsed list.
+     */
+
+    ckfree((char *) field);
+    *numLongsPtr = i;
     return propPtr;
 }
 
@@ -1476,9 +1473,8 @@ SelCvtToX(string, type, tkwin, numLongsPtr)
  *	procedure is the inverse of SelCvtToX.
  *
  * Results:
- *	The return value is the string equivalent of "property".  It is
- *	malloc-ed and should be freed by the caller when no longer
- *	needed.
+ *	The return value (stored in a Tcl_DString) is the string equivalent of
+ *	"property". It is up to the caller to initialize and free the DString.
  *
  * Side effects:
  *	None.
@@ -1486,60 +1482,34 @@ SelCvtToX(string, type, tkwin, numLongsPtr)
  *----------------------------------------------------------------------
  */
 
-static char *
-SelCvtFromX(propPtr, numValues, type, tkwin)
+static void
+SelCvtFromX(propPtr, numValues, type, tkwin, dsPtr)
     register long *propPtr;	/* Property value from X. */
     int numValues;		/* Number of 32-bit values in property. */
     Atom type;			/* Type of property  Should not be
 				 * XA_STRING (if so, don't bother calling
 				 * this procedure at all). */
     Tk_Window tkwin;		/* Window to use for atom conversion. */
+    Tcl_DString *dsPtr;		/* Where to store the converted string. */
 {
-    char *result;
-    int resultSpace, curSize, fieldSize;
-    CONST char *atomName;
-
     /*
-     * Convert each long in the property to a string value, which is
-     * either the name of an atom (if type is XA_ATOM) or a hexadecimal
-     * string.  Make an initial guess about the size of the result, but
-     * be prepared to enlarge the result if necessary.
+     * Convert each long in the property to a string value, which is either
+     * the name of an atom (if type is XA_ATOM) or a hexadecimal string. We
+     * build the list in a Tcl_DString because this is easier than trying to
+     * get the quoting correct ourselves; this is tricky because atoms can
+     * contain spaces in their names (encountered when the atoms are really
+     * MIME types). [Bug 1353414]
      */
 
-    resultSpace = 12*numValues+1;
-    curSize = 0;
-    atomName = "";	/* Not needed, but eliminates compiler warning. */
-    result = (char *) ckalloc((unsigned) resultSpace);
-    *result  = '\0';
     for ( ; numValues > 0; propPtr++, numValues--) {
 	if (type == XA_ATOM) {
-	    atomName = Tk_GetAtomName(tkwin, (Atom) *propPtr);
-	    fieldSize = strlen(atomName) + 1;
+	    Tcl_DStringAppendElement(dsPtr,
+		    Tk_GetAtomName(tkwin, (Atom) *propPtr));
 	} else {
-	    fieldSize = 12;
-	}
-	if (curSize+fieldSize >= resultSpace) {
-	    char *newResult;
+	    char buf[12];
 
-	    resultSpace *= 2;
-	    if (curSize+fieldSize >= resultSpace) {
-		resultSpace = curSize + fieldSize + 1;
-	    }
-	    newResult = (char *) ckalloc((unsigned) resultSpace);
-	    strncpy(newResult, result, (unsigned) curSize);
-	    ckfree(result);
-	    result = newResult;
-	}
-	if (curSize != 0) {
-	    result[curSize] = ' ';
-	    curSize++;
-	}
-	if (type == XA_ATOM) {
-	    strcpy(result+curSize, atomName);
-	} else {
-	    sprintf(result+curSize, "0x%x", (unsigned int) *propPtr);
+	    sprintf(buf, "0x%x", (unsigned int) *propPtr);
+	    Tcl_DStringAppendElement(dsPtr, buf);
 	}
-	curSize += strlen(result+curSize);
     }
-    return result;
 }
-- 
cgit v0.12