Tcl Source Code

View Ticket
Login
Ticket UUID: 414778
Title: Auto wrap Tcl_UtfToExternal
Type: Patch Version: None
Submitter: vcc Created on: 2001-04-09 01:43:18
Subsystem: 44. UTF-8 Strings Assigned To: andreas_kupries
Priority: 5 Medium Severity:
Status: Closed Last Modified: 2004-07-17 03:47:24
Resolution: Fixed Closed By: andreas_kupries
    Closed on: 2004-07-16 20:47:24
Description:
I wrote some packages use C/C++ , I must use 
Tcl_UtfToExternal convert in
args form UTF-8 to cp936 to made process chinese word 
in erveywhere. so I
hacked Tcl source code to auto do it.
It just simple, I add a flag needUtfToExternal to 
struct Command and
Tcl_Interp,Interp, and add a command 
needutftoexternal. if a C write package
need auto convert UTF to External just set the flag. 
like:
needutftoexternal 1
load mypackage.dll
......


--- ./tcl.h.orig Sun Apr  8 12:49:44 2001
+++ ./tcl.h Sun Apr  8 12:54:18 2001
@@ -369,6 +369,7 @@
     int errorLine;              /* When TCL_ERROR is 
returned, this gives
                                  * the line number 
within the command where
                                  * the error occurred 
(1 if first line). */
+    int needUtfToExternal; /* Zero means the the C 
Command  not need
UtfToExternal, other values need.*/
 } Tcl_Interp;

 typedef struct Tcl_AsyncHandler_ *Tcl_AsyncHandler;
--- ./tclBasic.c.orig Sun Apr  8 12:49:45 2001
+++ ./tclBasic.c Sun Apr  8 12:59:52 2001
@@ -449,6 +449,7 @@
      cmdPtr->deleteData = (ClientData) NULL;
      cmdPtr->deleted = 0;
      cmdPtr->importRefPtr = NULL;
+     cmdPtr->needUtfToExternal = 0;
      Tcl_SetHashValue(hPtr, cmdPtr);
  }
     }
@@ -1524,6 +1525,9 @@
      */

     TclResetShadowedCmdRefs(interp, cmdPtr);
+
+    cmdPtr->needUtfToExternal = interp-
>needUtfToExternal;
+
     return (Tcl_Command) cmdPtr;
 }

@@ -1685,6 +1689,9 @@
      */

     TclResetShadowedCmdRefs(interp, cmdPtr);
+
+    cmdPtr->needUtfToExternal = interp-
>needUtfToExternal;
+
     return (Tcl_Command) cmdPtr;
 }

--- ./tclExecute.c.orig Sun Apr  8 12:49:46 2001
+++ ./tclExecute.c Sun Apr  8 15:35:17 2001
@@ -842,8 +842,40 @@

   iPtr->cmdCount++;
   DECACHE_STACK_INFO();
+
+  if (cmdPtr->needUtfToExternal) {
+   for (i = 0; i < objc; i++) {
+    if (objv[i]->typePtr == NULL && objv[i]->length>0 
&& objv[i]->bytes) {
+     Tcl_DString ds;
+     int len;
+     Tcl_UtfToExternalDString(NULL,objv[i]->bytes,objv
[i]->length,&ds);
+     len = Tcl_DStringLength(&ds);
+     memcpy(objv[i]->bytes,Tcl_DStringValue(&ds),len);
+     objv[i]->bytes[len] = 0;
+     objv[i]->length = len;
+     Tcl_DStringFree(&ds);
+    }
+   }
+  }
+
   result = (*cmdPtr->objProc)(cmdPtr->objClientData, 
interp,
          objc, objv);
+
+  if (cmdPtr->needUtfToExternal) {
+   for (i = 0; i < objc; i++) {
+    if (objv[i]->typePtr == NULL && objv[i]->length>0 
&& objv[i]->bytes) {
+     Tcl_DString ds;
+     int len;
+     Tcl_ExternalToUtfDString(NULL,objv[i]->bytes,objv
[i]->length,&ds);
+     len = Tcl_DStringLength(&ds);
+     memcpy(objv[i]->bytes,Tcl_DStringValue(&ds),len);
+     objv[i]->bytes[len] = 0;
+     objv[i]->length = len;
+     Tcl_DStringFree(&ds);
+    }
+   }
+  }
+
   if (Tcl_AsyncReady()) {
       result = Tcl_AsyncInvoke(interp, result);
   }
--- ./tclInt.h.orig Sun Apr  8 12:49:47 2001
+++ ./tclInt.h Sun Apr  8 12:57:36 2001
@@ -1031,6 +1031,7 @@
      * command. The list is used to remove all
      * those imported commands when deleting
      * this "real" command. */
+ int needUtfToExternal; /* 1 : args need 
Tcl_UtfToExternal */
 } Command;

 /*
@@ -1106,6 +1107,9 @@
     int errorLine;  /* When TCL_ERROR is returned, 
this gives
      * the line number in the command where the
      * error occurred (1 means first line). */
+
+ int needUtfToExternal;
+
     struct TclStubs *stubTable;
     /* Pointer to the exported Tcl stub table.
      * On previous versions of Tcl this is a
--- ./tclInterp.c.orig Sun Apr  8 12:49:47 2001
+++ ./tclInterp.c Sun Apr  8 14:27:29 2001
@@ -191,6 +191,30 @@
 static void  SlaveObjCmdDeleteProc _ANSI_ARGS_((
        ClientData clientData));

+int Tcl_InterpNeedUtfToExternalCmd _ANSI_ARGS_
((ClientData clientData,
+      Tcl_Interp *interp, int argc, Tcl_Obj *CONST 
objv[]));
+
+
+int
+Tcl_InterpNeedUtfToExternalCmd(clientData, interp, 
objc, objv)
+    ClientData clientData;  /* Unused. */
+    Tcl_Interp *interp;   /* Current interpreter. */
+    int objc;        /* Number of arguments. */
+    Tcl_Obj *CONST objv[];  /* Argument objects. */
+{
+
+    Tcl_Obj* result = Tcl_GetObjResult(interp);
+    Tcl_SetIntObj(result,interp->needUtfToExternal);
+
+    if (objc >= 2) {
+        int need ;
+        Tcl_GetIntFromObj(interp,objv[1],&need);
+        interp->needUtfToExternal = need;
+    }
+
+    return TCL_OK;
+}
+
 /*

*------------------------------------------------------
---------------------
  *
@@ -231,13 +255,17 @@
     slavePtr->slaveInterp = interp;
     slavePtr->interpCmd  = NULL;
     Tcl_InitHashTable(&slavePtr->aliasTable, 
TCL_STRING_KEYS);
-
+
+    interp->needUtfToExternal = 0;
+
     Tcl_CreateObjCommand(interp, "interp", 
Tcl_InterpObjCmd, NULL, NULL);
+    Tcl_CreateObjCommand(interp, "needutftoexternal",
Tcl_InterpNeedUtfToExternalCmd, NULL, NULL);

     Tcl_CallWhenDeleted(interp, InterpInfoDeleteProc, 
NULL);
     return TCL_OK;
 }

+
 /*

*------------------------------------------------------
---------------------
  *
--- ./tcl.h.orig Sun Apr  8 12:49:44 2001
+++ ./tcl.h Sun Apr  8 12:54:18 2001
@@ -369,6 +369,7 @@
     int errorLine;              /* When TCL_ERROR is 
returned, this gives
                                  * the line number 
within the command where
                                  * the error occurred 
(1 if first line). */
+    int needUtfToExternal; /* Zero means the the C 
Command  not need
UtfToExternal, other values need.*/
 } Tcl_Interp;

 typedef struct Tcl_AsyncHandler_ *Tcl_AsyncHandler;
--- ./tclBasic.c.orig Sun Apr  8 12:49:45 2001
+++ ./tclBasic.c Sun Apr  8 12:59:52 2001
@@ -449,6 +449,7 @@
      cmdPtr->deleteData = (ClientData) NULL;
      cmdPtr->deleted = 0;
      cmdPtr->importRefPtr = NULL;
+     cmdPtr->needUtfToExternal = 0;
      Tcl_SetHashValue(hPtr, cmdPtr);
  }
     }
@@ -1524,6 +1525,9 @@
      */

     TclResetShadowedCmdRefs(interp, cmdPtr);
+
+    cmdPtr->needUtfToExternal = interp-
>needUtfToExternal;
+
     return (Tcl_Command) cmdPtr;
 }

@@ -1685,6 +1689,9 @@
      */

     TclResetShadowedCmdRefs(interp, cmdPtr);
+
+    cmdPtr->needUtfToExternal = interp-
>needUtfToExternal;
+
     return (Tcl_Command) cmdPtr;
 }

--- ./tclExecute.c.orig Sun Apr  8 12:49:46 2001
+++ ./tclExecute.c Sun Apr  8 15:35:17 2001
@@ -842,8 +842,40 @@

   iPtr->cmdCount++;
   DECACHE_STACK_INFO();
+
+  if (cmdPtr->needUtfToExternal) {
+   for (i = 0; i < objc; i++) {
+    if (objv[i]->typePtr == NULL && objv[i]->length>0 
&& objv[i]->bytes) {
+     Tcl_DString ds;
+     int len;
+     Tcl_UtfToExternalDString(NULL,objv[i]->bytes,objv
[i]->length,&ds);
+     len = Tcl_DStringLength(&ds);
+     memcpy(objv[i]->bytes,Tcl_DStringValue(&ds),len);
+     objv[i]->bytes[len] = 0;
+     objv[i]->length = len;
+     Tcl_DStringFree(&ds);
+    }
+   }
+  }
+
   result = (*cmdPtr->objProc)(cmdPtr->objClientData, 
interp,
          objc, objv);
+
+  if (cmdPtr->needUtfToExternal) {
+   for (i = 0; i < objc; i++) {
+    if (objv[i]->typePtr == NULL && objv[i]->length>0 
&& objv[i]->bytes) {
+     Tcl_DString ds;
+     int len;
+     Tcl_ExternalToUtfDString(NULL,objv[i]->bytes,objv
[i]->length,&ds);
+     len = Tcl_DStringLength(&ds);
+     memcpy(objv[i]->bytes,Tcl_DStringValue(&ds),len);
+     objv[i]->bytes[len] = 0;
+     objv[i]->length = len;
+     Tcl_DStringFree(&ds);
+    }
+   }
+  }
+
   if (Tcl_AsyncReady()) {
       result = Tcl_AsyncInvoke(interp, result);
   }
--- ./tclInt.h.orig Sun Apr  8 12:49:47 2001
+++ ./tclInt.h Sun Apr  8 12:57:36 2001
@@ -1031,6 +1031,7 @@
      * command. The list is used to remove all
      * those imported commands when deleting
      * this "real" command. */
+ int needUtfToExternal; /* 1 : args need 
Tcl_UtfToExternal */
 } Command;

 /*
@@ -1106,6 +1107,9 @@
     int errorLine;  /* When TCL_ERROR is returned, 
this gives
      * the line number in the command where the
      * error occurred (1 means first line). */
+
+ int needUtfToExternal;
+
     struct TclStubs *stubTable;
     /* Pointer to the exported Tcl stub table.
      * On previous versions of Tcl this is a
--- ./tclInterp.c.orig Sun Apr  8 12:49:47 2001
+++ ./tclInterp.c Sun Apr  8 14:27:29 2001
@@ -191,6 +191,30 @@
 static void  SlaveObjCmdDeleteProc _ANSI_ARGS_((
        ClientData clientData));

+int Tcl_InterpNeedUtfToExternalCmd _ANSI_ARGS_
((ClientData clientData,
+      Tcl_Interp *interp, int argc, Tcl_Obj *CONST 
objv[]));
+
+
+int
+Tcl_InterpNeedUtfToExternalCmd(clientData, interp, 
objc, objv)
+    ClientData clientData;  /* Unused. */
+    Tcl_Interp *interp;   /* Current interpreter. */
+    int objc;        /* Number of arguments. */
+    Tcl_Obj *CONST objv[];  /* Argument objects. */
+{
+
+    Tcl_Obj* result = Tcl_GetObjResult(interp);
+    Tcl_SetIntObj(result,interp->needUtfToExternal);
+
+    if (objc >= 2) {
+        int need ;
+        Tcl_GetIntFromObj(interp,objv[1],&need);
+        interp->needUtfToExternal = need;
+    }
+
+    return TCL_OK;
+}
+
 /*

*------------------------------------------------------
---------------------
  *
@@ -231,13 +255,17 @@
     slavePtr->slaveInterp = interp;
     slavePtr->interpCmd  = NULL;
     Tcl_InitHashTable(&slavePtr->aliasTable, 
TCL_STRING_KEYS);
-
+
+    interp->needUtfToExternal = 0;
+
     Tcl_CreateObjCommand(interp, "interp", 
Tcl_InterpObjCmd, NULL, NULL);
+    Tcl_CreateObjCommand(interp, "needutftoexternal",
Tcl_InterpNeedUtfToExternalCmd, NULL, NULL);

     Tcl_CallWhenDeleted(interp, InterpInfoDeleteProc, 
NULL);
     return TCL_OK;
 }

+
 /*

*------------------------------------------------------
---------------------
  *
--- tcl.h.orig Sun Apr  8 12:49:44 2001
+++ tcl.h Sun Apr  8 12:54:18 2001
@@ -369,6 +369,7 @@
     int errorLine;              /* When TCL_ERROR is 
returned, this gives
                                  * the line number 
within the command where
                                  * the error occurred 
(1 if first line). */
+    int needUtfToExternal; /* Zero means the the C 
Command  not need
UtfToExternal, other values need.*/
 } Tcl_Interp;

 typedef struct Tcl_AsyncHandler_ *Tcl_AsyncHandler;
--- tclBasic.c.orig Sun Apr  8 12:49:45 2001
+++ tclBasic.c Sun Apr  8 12:59:52 2001
@@ -449,6 +449,7 @@
      cmdPtr->deleteData = (ClientData) NULL;
      cmdPtr->deleted = 0;
      cmdPtr->importRefPtr = NULL;
+     cmdPtr->needUtfToExternal = 0;
      Tcl_SetHashValue(hPtr, cmdPtr);
  }
     }
@@ -1524,6 +1525,9 @@
      */

     TclResetShadowedCmdRefs(interp, cmdPtr);
+
+    cmdPtr->needUtfToExternal = interp-
>needUtfToExternal;
+
     return (Tcl_Command) cmdPtr;
 }

@@ -1685,6 +1689,9 @@
      */

     TclResetShadowedCmdRefs(interp, cmdPtr);
+
+    cmdPtr->needUtfToExternal = interp-
>needUtfToExternal;
+
     return (Tcl_Command) cmdPtr;
 }

--- tclExecute.c.orig Sun Apr  8 12:49:46 2001
+++ tclExecute.c Sun Apr  8 15:35:17 2001
@@ -842,8 +842,40 @@

   iPtr->cmdCount++;
   DECACHE_STACK_INFO();
+
+  if (cmdPtr->needUtfToExternal) {
+   for (i = 0; i < objc; i++) {
+    if (objv[i]->typePtr == NULL && objv[i]->length>0 
&& objv[i]->bytes) {
+     Tcl_DString ds;
+     int len;
+     Tcl_UtfToExternalDString(NULL,objv[i]->bytes,objv
[i]->length,&ds);
+     len = Tcl_DStringLength(&ds);
+     memcpy(objv[i]->bytes,Tcl_DStringValue(&ds),len);
+     objv[i]->bytes[len] = 0;
+     objv[i]->length = len;
+     Tcl_DStringFree(&ds);
+    }
+   }
+  }
+
   result = (*cmdPtr->objProc)(cmdPtr->objClientData, 
interp,
          objc, objv);
+
+  if (cmdPtr->needUtfToExternal) {
+   for (i = 0; i < objc; i++) {
+    if (objv[i]->typePtr == NULL && objv[i]->length>0 
&& objv[i]->bytes) {
+     Tcl_DString ds;
+     int len;
+     Tcl_ExternalToUtfDString(NULL,objv[i]->bytes,objv
[i]->length,&ds);
+     len = Tcl_DStringLength(&ds);
+     memcpy(objv[i]->bytes,Tcl_DStringValue(&ds),len);
+     objv[i]->bytes[len] = 0;
+     objv[i]->length = len;
+     Tcl_DStringFree(&ds);
+    }
+   }
+  }
+
   if (Tcl_AsyncReady()) {
       result = Tcl_AsyncInvoke(interp, result);
   }
--- tclInt.h.orig Sun Apr  8 12:49:47 2001
+++ tclInt.h Sun Apr  8 12:57:36 2001
@@ -1031,6 +1031,7 @@
      * command. The list is used to remove all
      * those imported commands when deleting
      * this "real" command. */
+ int needUtfToExternal; /* 1 : args need 
Tcl_UtfToExternal */
 } Command;

 /*
@@ -1106,6 +1107,9 @@
     int errorLine;  /* When TCL_ERROR is returned, 
this gives
      * the line number in the command where the
      * error occurred (1 means first line). */
+
+ int needUtfToExternal;
+
     struct TclStubs *stubTable;
     /* Pointer to the exported Tcl stub table.
      * On previous versions of Tcl this is a
--- tclInterp.c.orig Sun Apr  8 12:49:47 2001
+++ tclInterp.c Sun Apr  8 14:27:29 2001
@@ -191,6 +191,30 @@
 static void  SlaveObjCmdDeleteProc _ANSI_ARGS_((
        ClientData clientData));

+int Tcl_InterpNeedUtfToExternalCmd _ANSI_ARGS_
((ClientData clientData,
+      Tcl_Interp *interp, int argc, Tcl_Obj *CONST 
objv[]));
+
+
+int
+Tcl_InterpNeedUtfToExternalCmd(clientData, interp, 
objc, objv)
+    ClientData clientData;  /* Unused. */
+    Tcl_Interp *interp;   /* Current interpreter. */
+    int objc;        /* Number of arguments. */
+    Tcl_Obj *CONST objv[];  /* Argument objects. */
+{
+
+    Tcl_Obj* result = Tcl_GetObjResult(interp);
+    Tcl_SetIntObj(result,interp->needUtfToExternal);
+
+    if (objc >= 2) {
+        int need ;
+        Tcl_GetIntFromObj(interp,objv[1],&need);
+        interp->needUtfToExternal = need;
+    }
+
+    return TCL_OK;
+}
+
 /*

*------------------------------------------------------
---------------------
  *
@@ -231,13 +255,17 @@
     slavePtr->slaveInterp = interp;
     slavePtr->interpCmd  = NULL;
     Tcl_InitHashTable(&slavePtr->aliasTable, 
TCL_STRING_KEYS);
-
+
+    interp->needUtfToExternal = 0;
+
     Tcl_CreateObjCommand(interp, "interp", 
Tcl_InterpObjCmd, NULL, NULL);
+    Tcl_CreateObjCommand(interp, "needutftoexternal",
Tcl_InterpNeedUtfToExternalCmd, NULL, NULL);

     Tcl_CallWhenDeleted(interp, InterpInfoDeleteProc, 
NULL);
     return TCL_OK;
 }

+
 /*

*------------------------------------------------------
---------------------
  *
User Comments: andreas_kupries added on 2004-07-17 03:47:24:
Logged In: YES 
user_id=75003

Added a note to the documentation of Tcl_CreateCommand, both
head and 8.4 branches.

davygrvy added on 2001-12-15 03:18:08:
Logged In: YES 
user_id=7549

Wei,

As a final note, Tcl_CreateObjCommand() is much prefered to 
Tcl_CreateCommand().  I'm only assuming you are using 
Tcl_CreateCommand() as I had a similar issue once.

If you are using Tcl_CreateObjCommand() already, yes, you 
will need to prepare the string each and every time with 
Tcl_UtfToExternalDString() as strings internally to Tcl are 
meant to be in neutral UTF-8.

for (i = 0; i < objc; i++) { 
...
Tcl_ExternalToUtfDString(NULL,objv[i]->bytes,objv 
[i]->length,&ds);
...
}

In your patch, looping over the objv array and twiddling 
with the bytes directly assumes each obj has a valid string 
rep and kind of breaks the Tcl_Obj concept.

If you could show us an example of your extension code, I 
could help advise a fix for you.  If you are running on 
windows only, you could use Tcl_WinUtfToTChar to prep the 
string.  This will also switch to use unicode if running on 
NT.

Yes, this conversion stuff is tricky, but well worth the 
effort to get right.

andreas_kupries added on 2001-12-15 02:25:03:
Logged In: YES 
user_id=75003

Given that relink is required anyway I would opt for
updating the documentation and mentioning this in the
upgrade guide 7 -> 8.2. Especially there!

davygrvy added on 2001-12-15 02:18:28:

File Added - 14708: patch.txt

Logged In: YES 
user_id=7549

Basically, an extension that works fine in 7.6, all of a 
sudden gets these funky UTF-8 special chars when run in 
8.1+.  It's interesting to note that to run in 8.1+, you 
need to re-link anyways.  Change the docs or change 
TclInvokeStringCommand() to be back like it once was?  I'm 
only bringing up the real issue.  I can't be the one to 
make the descision.

davygrvy added on 2001-12-15 01:52:09:
Logged In: YES 
user_id=7549

TclInvokeStringCommand() in tclBasic.c (line 1735) is used 
for the Tcl_ObjCmdProc for Tcl_CmdProc commands as the 
global wrapper.  wei's change should possibly go in here 
for changing the meaning of the argv[] array from UTF-8 
back to system codepage (as in 7.6).

Now I'm not saying I think the core needs a change for 
this, but I do agree that the behavior did change and 
wasn't documented.

davygrvy added on 2001-12-15 01:09:57:
Logged In: YES 
user_id=7549

Andreas,

In a way I understand what he's doing.  The old 
Tcl_CmdProc's argv[] array underhandedly changed to mean an 
array in UTF-8 in 8.1.  It looks like wei is fixing argv[] 
to mean the old char* in system codepage.  I did something 
similar once.  I'll do some research and post back.

andreas_kupries added on 2001-08-25 00:55:04:
Logged In: YES 
user_id=75003

I do not believe that he is doing the right thing, but 
can't lay my finger on it.

Attachments: