From 46c246b94240ad0b0015045bc5fe5bddf4b435bf Mon Sep 17 00:00:00 2001
From: hobbs <hobbs>
Date: Tue, 7 Mar 2000 22:27:44 +0000
Subject: 	* tests/entry.test: 	* generic/tkEntry.c (EntrySetValue):
 malloc the value when 	validating because validation could cause the pointer
 to become 	invalid.  Also fixed configure to not trigger focus-based 
 validation.  Improved use of Tcl_WrongNumArgs. [Bug: 4320]

---
 ChangeLog         |   8 ++++
 generic/tkEntry.c | 115 ++++++++++++++++++++++++++++++------------------------
 tests/entry.test  |   4 +-
 3 files changed, 74 insertions(+), 53 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 7262783..83764b8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2000-03-07  Jeff Hobbs  <hobbs@scriptics.com>
+
+	* tests/entry.test:
+	* generic/tkEntry.c (EntrySetValue): malloc the value when
+	validating because validation could cause the pointer to become
+	invalid.  Also fixed configure to not trigger focus-based
+	validation.  Improved use of Tcl_WrongNumArgs. [Bug: 4320]
+
 2000-03-06  Eric Melski  <ericm@scriptics.com>
 
 	* generic/tkOldConfig.c: Added check for NULL tkwin value in
diff --git a/generic/tkEntry.c b/generic/tkEntry.c
index d860289..3dcd5a2 100644
--- a/generic/tkEntry.c
+++ b/generic/tkEntry.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: tkEntry.c,v 1.10 2000/03/02 21:52:40 hobbs Exp $
+ * RCS: @(#) $Id: tkEntry.c,v 1.11 2000/03/07 22:27:46 hobbs Exp $
  */
 
 #include "tkInt.h"
@@ -390,7 +390,7 @@ static char *selCommandNames[] = {
 };
 
 enum selcommand {
-    SELECTION_ADJUST, SELECTION_CLEAR, SELECTION_FROM, 
+    SELECTION_ADJUST, SELECTION_CLEAR, SELECTION_FROM,
     SELECTION_PRESENT, SELECTION_RANGE, SELECTION_TO
 };
 
@@ -433,8 +433,8 @@ static int		EntryValidate _ANSI_ARGS_((Entry *entryPtr,
 static int		EntryValidateChange _ANSI_ARGS_((Entry *entryPtr,
 			    char *change, char *new, int index, int type));
 static void		ExpandPercents _ANSI_ARGS_((Entry *entryPtr,
-			    char *before, char *change, char *new, int index,
-			    int type, Tcl_DString *dsPtr));
+			    char *before, char *change, char *new,
+			    int index, int type, Tcl_DString *dsPtr));
 #endif /* ENTRY_VALIDATE */
 static void		EntryValueChanged _ANSI_ARGS_((Entry *entryPtr));
 static void		EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,
@@ -667,7 +667,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 	    char buf[TCL_INTEGER_SPACE * 4];
 
 	    if (objc != 3) {
-	        Tcl_WrongNumArgs(interp, 1, objv, "bbox index");
+	        Tcl_WrongNumArgs(interp, 2, objv, "index");
 		goto error;
 	    }
 	    if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 
@@ -687,7 +687,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 	
         case COMMAND_CGET: {
 	    if (objc != 3) {
-	        Tcl_WrongNumArgs(interp, 1, objv, "cget option");
+	        Tcl_WrongNumArgs(interp, 2, objv, "option");
 		goto error;
 	    }
 	    
@@ -722,8 +722,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 	    int first, last;
 
 	    if ((objc < 3) || (objc > 4)) {
-	        Tcl_WrongNumArgs(interp, 1, objv, 
-                        "delete firstIndex ?lastIndex?");
+	        Tcl_WrongNumArgs(interp, 2, objv, "firstIndex ?lastIndex?");
 		goto error;
 	    }
 	    if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 
@@ -746,7 +745,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 
         case COMMAND_GET: {
 	    if (objc != 2) {
-	        Tcl_WrongNumArgs(interp, 1, objv, "get");
+	        Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);
 		goto error;
 	    }
 	    Tcl_SetResult(interp, entryPtr->string, TCL_STATIC);
@@ -755,7 +754,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 
         case COMMAND_ICURSOR: {
 	    if (objc != 3) {
-	        Tcl_WrongNumArgs(interp, 1, objv, "icursor pos");
+	        Tcl_WrongNumArgs(interp, 2, objv, "pos");
 		goto error;
 	    }
 	    if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
@@ -768,18 +767,16 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 	
         case COMMAND_INDEX: {
 	    int index;
-	    char buf[TCL_INTEGER_SPACE];
 
 	    if (objc != 3) {
-	        Tcl_WrongNumArgs(interp, 1, objv, "index string");
+	        Tcl_WrongNumArgs(interp, 2, objv, "string");
 		goto error;
 	    }
 	    if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 
                     &index) != TCL_OK) {
 	        goto error;
 	    }
-	    sprintf(buf, "%d", index);
-	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
+	    Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
 	    break;
 	}
 
@@ -787,7 +784,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 	    int index;
 
 	    if (objc != 4) {
-	        Tcl_WrongNumArgs(interp, 1, objv, "insert index text");
+	        Tcl_WrongNumArgs(interp, 2, objv, "index text");
 		goto error;
 	    }
 	    if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 
@@ -805,7 +802,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 	    char *minorCmd;
 
 	    if (objc != 4) {
-	        Tcl_WrongNumArgs(interp, 1, objv, "scan mark|dragto x");
+	        Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x");
 		goto error;
 	    }
 	    if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) {
@@ -833,7 +830,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 	    int index, index2;
 
 	    if (objc < 3) {
-	        Tcl_WrongNumArgs(interp, 1, objv, "select option ?index?");
+	        Tcl_WrongNumArgs(interp, 2, objv, "option ?index?");
 		goto error;
 	    }
 
@@ -851,8 +848,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 	    switch(selIndex) {
 	        case SELECTION_ADJUST: {
 		    if (objc != 4) {
-		        Tcl_WrongNumArgs(interp, 1, objv, 
-                                "selection adjust index");
+		        Tcl_WrongNumArgs(interp, 3, objv, "index");
 			goto error;
 		    }
 		    if (GetEntryIndex(interp, entryPtr, 
@@ -883,7 +879,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 
 	        case SELECTION_CLEAR: {
 		    if (objc != 3) {
-		        Tcl_WrongNumArgs(interp, 1, objv, "selection clear");
+		        Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);
 			goto error;
 		    }
 		    if (entryPtr->selectFirst >= 0) {
@@ -896,8 +892,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 
 	        case SELECTION_FROM: {
 		    if (objc != 4) {
-		        Tcl_WrongNumArgs(interp, 1, objv, 
-			        "selection from index");
+		        Tcl_WrongNumArgs(interp, 3, objv, "index");
 			goto error;
 		    }
 		    if (GetEntryIndex(interp, entryPtr, 
@@ -910,7 +905,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 
 	        case SELECTION_PRESENT: {
 		    if (objc != 3) {
-		        Tcl_WrongNumArgs(interp, 1, objv, "selection present");
+		        Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);
 			goto error;
 		    }
 		    if (entryPtr->selectFirst < 0) {
@@ -923,8 +918,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 
 	        case SELECTION_RANGE: {
 		    if (objc != 5) {
-		        Tcl_WrongNumArgs(interp, 1, objv, 
-                                "selection range start end");
+		        Tcl_WrongNumArgs(interp, 3, objv, "start end");
 			goto error;
 		    }
 		    if (GetEntryIndex(interp, entryPtr, 
@@ -954,8 +948,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 		
 	        case SELECTION_TO: {
 		    if (objc != 4) {
-		        Tcl_WrongNumArgs(interp, 1, objv, 
-                                "selection to index");
+		        Tcl_WrongNumArgs(interp, 3, objv, "index");
 			goto error;
 		    }
 		    if (GetEntryIndex(interp, entryPtr, 
@@ -974,7 +967,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
 	    int code;
 
 	    if (objc != 2) {
-		Tcl_WrongNumArgs(interp, 1, objv, "validate");
+		Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);
 		goto error;
 	    }
 	    selIndex = entryPtr->validate;
@@ -1203,11 +1196,15 @@ ConfigureEntry(interp, entryPtr, objc, objv, flags)
 
 	/*
 	 * Restart the cursor timing sequence in case the on-time or 
-	 * off-time just changed.
+	 * off-time just changed.  Set validate temporarily to none,
+	 * so the configure doesn't cause it to be triggered.
 	 */
 
 	if (entryPtr->flags & GOT_FOCUS) {
-	  EntryFocusProc(entryPtr, 1);
+	    int validate = entryPtr->validate;
+	    entryPtr->validate = VALIDATE_NONE;
+	    EntryFocusProc(entryPtr, 1);
+	    entryPtr->validate = validate;
 	}
 
 	/*
@@ -1964,18 +1961,29 @@ EntrySetValue(entryPtr, value)
 {
     char *oldSource;
 #ifdef ENTRY_VALIDATE
-    int code;
+    int code, valueLen, malloced = 0;
 
     if (strcmp(value, entryPtr->string) == 0) {
 	return;
     }
+    valueLen = strlen(value);
 
     if (entryPtr->flags & VALIDATE_VAR) {
 	entryPtr->flags |= VALIDATE_ABORT;
     } else {
+	/*
+	 * If we validate, we create a copy of the value, as it may
+	 * point to volatile memory, like the value of the -textvar
+	 * which may get freed during validation
+	 */
+	oldSource = (char *) ckalloc((unsigned) (valueLen + 1));
+	strcpy(oldSource, value);
+	value = oldSource;
+	malloced = 1;
+
 	entryPtr->flags |= VALIDATE_VAR;
 	code = EntryValidateChange(entryPtr, (char *) NULL, value, -1,
-				   VALIDATE_FORCED);
+		VALIDATE_FORCED);
 	entryPtr->flags &= ~VALIDATE_VAR;
 	/*
 	 * If VALIDATE_ABORT has been set, then this operation should be
@@ -1983,19 +1991,23 @@ EntrySetValue(entryPtr, value)
 	 */
 	if (entryPtr->flags & VALIDATE_ABORT) {
 	    entryPtr->flags &= ~VALIDATE_ABORT;
+	    ckfree(value);
 	    return;
 	}
     }
 #endif /* ENTRY_VALIDATE */
 
     oldSource = entryPtr->string;
-
     ckfree(entryPtr->string);
-    entryPtr->numBytes = strlen(value);
-    entryPtr->numChars = Tcl_NumUtfChars(value, entryPtr->numBytes);
-    entryPtr->string =
-	    (char *) ckalloc((unsigned) (entryPtr->numBytes + 1));
-    strcpy(entryPtr->string, value);
+
+    if (malloced) {
+	entryPtr->string = value;
+    } else {
+	entryPtr->string   = (char *) ckalloc((unsigned) (valueLen + 1));
+	strcpy(entryPtr->string, value);
+    }
+    entryPtr->numBytes = valueLen;
+    entryPtr->numChars = Tcl_NumUtfChars(value, valueLen);
 
     if (entryPtr->displayString == oldSource) {
 	entryPtr->displayString = entryPtr->string;
@@ -2767,7 +2779,7 @@ EntryValidate(entryPtr, cmd)
     register Tcl_Interp *interp = entryPtr->interp;
     int code, bool;
 
-    code = Tcl_GlobalEval(interp, cmd);
+    code = Tcl_EvalEx(interp, cmd, -1, TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT);
 
     if (code != TCL_OK && code != TCL_RETURN) {
 	Tcl_AddErrorInfo(interp,
@@ -2779,13 +2791,13 @@ EntryValidate(entryPtr, cmd)
     if (Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp),
 			      &bool) != TCL_OK) {
 	Tcl_AddErrorInfo(interp,
-		 "\nValid Tcl Boolean not returned by validation command");
+		 "\nvalid boolean not returned by validation command");
 	Tcl_BackgroundError(interp);
-	Tcl_SetObjLength(Tcl_GetObjResult(interp), 0);
+	Tcl_SetResult(interp, NULL, 0);
 	return TCL_ERROR;
     }
 
-    Tcl_SetObjLength(Tcl_GetObjResult(interp), 0);
+    Tcl_SetResult(interp, NULL, 0);
     return (bool ? TCL_OK : TCL_BREAK);
 }
 
@@ -2846,7 +2858,7 @@ EntryValidateChange(entryPtr, change, new, index, type)
 
     Tcl_DStringInit(&script);
     ExpandPercents(entryPtr, entryPtr->validateCmd,
-		   change, new, index, type, &script);
+	    change, new, index, type, &script);
     Tcl_DStringAppend(&script, "", 1);
 
     p = Tcl_DStringValue(&script);
@@ -2887,7 +2899,8 @@ EntryValidateChange(entryPtr, change, new, index, type)
 			   change, new, index, type, &script);
 	    Tcl_DStringAppend(&script, "", 1);
 	    p = Tcl_DStringValue(&script);
-	    if (Tcl_GlobalEval(entryPtr->interp, p) != TCL_OK) {
+	    if (Tcl_EvalEx(entryPtr->interp, p, -1,
+		    TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT) != TCL_OK) {
 		Tcl_AddErrorInfo(entryPtr->interp,
 				 "\n\t(in invalidcommand executed by entry)");
 		Tcl_BackgroundError(entryPtr->interp);
@@ -2927,12 +2940,12 @@ ExpandPercents(entryPtr, before, change, new, index, type, dsPtr)
      register Entry *entryPtr;	/* Entry that needs validation. */
      register char *before;	/* Command containing percent
 				 * expressions to be replaced. */
-     char *change; 		/* Characters to added/deleted
+     char *change;		/* Characters to added/deleted
 				 * (NULL-terminated string). */
-     char *new;                 /* Potential new value of entry string */
-     int index;                 /* index of insert/delete */
-     int type;                  /* INSERT or DELETE */
-     Tcl_DString *dsPtr;        /* Dynamic string in which to append
+     char *new;			/* Potential new value of entry string */
+     int index;			/* index of insert/delete */
+     int type;			/* INSERT or DELETE */
+     Tcl_DString *dsPtr;	/* Dynamic string in which to append
 				 * new command. */
 {
     int spaceNeeded, cvtFlags;	/* Used to substitute string as proper Tcl
@@ -3028,10 +3041,10 @@ ExpandPercents(entryPtr, before, change, new, index, type, dsPtr)
 	    break;
 	}
 
-	spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
+	spaceNeeded = Tcl_ScanCountedElement(string, -1, &cvtFlags);
 	length = Tcl_DStringLength(dsPtr);
 	Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
-	spaceNeeded = Tcl_ConvertElement(string,
+	spaceNeeded = Tcl_ConvertCountedElement(string, -1,
 		Tcl_DStringValue(dsPtr) + length,
 		cvtFlags | TCL_DONT_USE_BRACES);
 	Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
diff --git a/tests/entry.test b/tests/entry.test
index f90e5b4..f8fd7ef 100644
--- a/tests/entry.test
+++ b/tests/entry.test
@@ -6,7 +6,7 @@
 # Copyright (c) 1998-1999 by Scriptics Corporation.
 # All rights reserved.
 #
-# RCS: @(#) $Id: entry.test,v 1.6 2000/03/02 21:52:26 hobbs Exp $
+# RCS: @(#) $Id: entry.test,v 1.7 2000/03/07 22:27:50 hobbs Exp $
 
 if {[lsearch [namespace children] ::tcltest] == -1} {
     source [file join [pwd] [file dirname [info script]] defs.tcl]
@@ -338,7 +338,7 @@ test entry-3.46 {EntryWidgetCmd procedure, "scan" widget command} {fonts} {
 } {2}
 test entry-3.47 {EntryWidgetCmd procedure, "select" widget command} {
     list [catch {.e select} msg] $msg
-} {1 {wrong # args: should be ".e select option ?index?"}}
+} {1 {wrong # args: should be ".e selection option ?index?"}}
 test entry-3.48 {EntryWidgetCmd procedure, "select" widget command} {
     list [catch {.e select foo} msg] $msg
 } {1 {bad selection option "foo": must be adjust, clear, from, present, range, or to}}
-- 
cgit v0.12