From d8dbc22dc73a294980ff236d1cef977635fc62a1 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Thu, 24 Apr 2025 09:51:35 +0000 Subject: Implement "encoding user" without TIP #716 --- generic/tclCmdAH.c | 39 +++++++++++++++++++++++++++++++++++++++ generic/tclInt.decls | 3 +++ generic/tclIntPlatDecls.h | 8 +++++--- generic/tclStubInit.c | 6 +++++- tests/cmdAH.test | 2 +- win/tclWinInit.c | 30 ++++++++++++++++++++++++++++++ 6 files changed, 83 insertions(+), 5 deletions(-) diff --git a/generic/tclCmdAH.c b/generic/tclCmdAH.c index ea98a83..9970b0e 100644 --- a/generic/tclCmdAH.c +++ b/generic/tclCmdAH.c @@ -53,6 +53,11 @@ static Tcl_ObjCmdProc EncodingDirsObjCmd; static Tcl_ObjCmdProc EncodingNamesObjCmd; static Tcl_ObjCmdProc EncodingProfilesObjCmd; static Tcl_ObjCmdProc EncodingSystemObjCmd; +#ifdef _WIN32 +static Tcl_ObjCmdProc EncodingUserObjCmd; +#else +# define EncodingUserObjCmd EncodingSystemObjCmd +#endif static inline int ForeachAssignments(Tcl_Interp *interp, struct ForeachState *statePtr); static inline void ForeachCleanup(Tcl_Interp *interp, @@ -394,6 +399,7 @@ TclInitEncodingCmd( {"names", EncodingNamesObjCmd, TclCompileBasic0ArgCmd, NULL, NULL, 0}, {"profiles", EncodingProfilesObjCmd, TclCompileBasic0ArgCmd, NULL, NULL, 0}, {"system", EncodingSystemObjCmd, TclCompileBasic0Or1ArgCmd, NULL, NULL, 1}, + {"user", EncodingUserObjCmd, TclCompileBasic0ArgCmd, NULL, NULL, 1}, {NULL, NULL, NULL, NULL, NULL, 0} }; @@ -829,6 +835,39 @@ EncodingSystemObjCmd( } /* + *----------------------------------------------------------------------------- + * + * EncodingUserObjCmd -- + * + * This command retrieves the encoding as per the user settings. + * + * Results: + * Returns a standard Tcl result + * + *----------------------------------------------------------------------------- + */ + +#ifdef _WIN32 +int +EncodingUserObjCmd( + TCL_UNUSED(void *), + Tcl_Interp* interp, /* Tcl interpreter */ + int objc, /* Number of command line args */ + Tcl_Obj* const objv[]) /* Vector of command line args */ +{ + if (objc != 1) { + Tcl_WrongNumArgs(interp, 1, objv, ""); + return TCL_ERROR; + } + const char *encodingName = Tcl_GetEncodingName(TclWinGetUserEncoding(interp)); + if (encodingName) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(encodingName, -1)); + } + return TCL_OK; +} +#endif + +/* *---------------------------------------------------------------------- * * Tcl_ErrorObjCmd -- diff --git a/generic/tclInt.decls b/generic/tclInt.decls index 17cad13..a0efa13 100644 --- a/generic/tclInt.decls +++ b/generic/tclInt.decls @@ -683,6 +683,9 @@ declare 8 { declare 9 { TclFile TclpCreateTempFile(const char *contents) } +declare 10 { + Tcl_Encoding TclWinGetUserEncoding(Tcl_Interp *interp) +} declare 11 { void TclGetAndDetachPids(Tcl_Interp *interp, Tcl_Channel chan) } diff --git a/generic/tclIntPlatDecls.h b/generic/tclIntPlatDecls.h index aab3737..ebc5896 100644 --- a/generic/tclIntPlatDecls.h +++ b/generic/tclIntPlatDecls.h @@ -547,7 +547,8 @@ EXTERN TclFile TclpOpenFile(const char *fname, int mode); EXTERN Tcl_Size TclpGetPid(Tcl_Pid pid); /* 9 */ EXTERN TclFile TclpCreateTempFile(const char *contents); -/* Slot 10 is reserved */ +/* 10 */ +EXTERN Tcl_Encoding TclWinGetUserEncoding(Tcl_Interp *interp); /* 11 */ EXTERN void TclGetAndDetachPids(Tcl_Interp *interp, Tcl_Channel chan); @@ -600,7 +601,7 @@ typedef struct TclIntPlatStubs { TclFile (*tclpOpenFile) (const char *fname, int mode); /* 7 */ Tcl_Size (*tclpGetPid) (Tcl_Pid pid); /* 8 */ TclFile (*tclpCreateTempFile) (const char *contents); /* 9 */ - void (*reserved10)(void); + Tcl_Encoding (*tclWinGetUserEncoding) (Tcl_Interp *interp); /* 10 */ void (*tclGetAndDetachPids) (Tcl_Interp *interp, Tcl_Channel chan); /* 11 */ void (*reserved12)(void); void (*reserved13)(void); @@ -654,7 +655,8 @@ extern const TclIntPlatStubs *tclIntPlatStubsPtr; (tclIntPlatStubsPtr->tclpGetPid) /* 8 */ #define TclpCreateTempFile \ (tclIntPlatStubsPtr->tclpCreateTempFile) /* 9 */ -/* Slot 10 is reserved */ +#define TclWinGetUserEncoding \ + (tclIntPlatStubsPtr->tclWinGetUserEncoding) /* 10 */ #define TclGetAndDetachPids \ (tclIntPlatStubsPtr->tclGetAndDetachPids) /* 11 */ /* Slot 12 is reserved */ diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index 9bfce36..73e8765 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -69,6 +69,10 @@ # undef Tcl_WinConvertError # define Tcl_WinConvertError 0 #endif +#if !defined(_WIN32) /* TODO: implement for Cygwin too */ +# undef TclWinGetUserEncoding +# define TclWinGetUserEncoding 0 +#endif #undef TclGetStringFromObj #if defined(TCL_NO_DEPRECATED) # define TclGetStringFromObj 0 @@ -692,7 +696,7 @@ static const TclIntPlatStubs tclIntPlatStubs = { TclpOpenFile, /* 7 */ TclpGetPid, /* 8 */ TclpCreateTempFile, /* 9 */ - 0, /* 10 */ + TclWinGetUserEncoding, /* 10 */ TclGetAndDetachPids, /* 11 */ 0, /* 12 */ 0, /* 13 */ diff --git a/tests/cmdAH.test b/tests/cmdAH.test index 50e49e8..7b9e4f3 100644 --- a/tests/cmdAH.test +++ b/tests/cmdAH.test @@ -323,7 +323,7 @@ test cmdAH-4.1.1 {encoding} -returnCodes error -body { } -result {wrong # args: should be "encoding subcommand ?arg ...?"} test cmdAH-4.1.2 {Tcl_EncodingObjCmd} -returnCodes error -body { encoding foo -} -result {unknown or ambiguous subcommand "foo": must be convertfrom, convertto, dirs, names, profiles, or system} +} -result {unknown or ambiguous subcommand "foo": must be convertfrom, convertto, dirs, names, profiles, system, or user} # # encoding system 4.2.* diff --git a/win/tclWinInit.c b/win/tclWinInit.c index 141aff1..c2eb9d7 100644 --- a/win/tclWinInit.c +++ b/win/tclWinInit.c @@ -397,6 +397,36 @@ TclpSetInitialEncodings(void) Tcl_DStringFree(&encodingName); } +static Tcl_Encoding userEncoding = NULL; + +Tcl_Encoding +TclWinGetUserEncoding(Tcl_Interp *interp) +{ + WCHAR buf[32] = L"cp"; + + HKEY hKey; + DWORD type = -1; + DWORD size=sizeof(buf) - 2; + Tcl_DString ds; + RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage", + 0, KEY_READ, &hKey); + RegQueryValueExW(hKey, L"ACP", NULL, &type, (BYTE *)&buf[2], &size); + RegCloseKey(hKey); + if (!wcscmp(buf, L"cp65001")) { + wcscpy(buf, L"utf-8"); + } + Tcl_DStringInit(&ds); + Tcl_WCharToUtfDString(buf, -1, &ds); + if (!userEncoding || strcmp(Tcl_GetEncodingName(userEncoding), Tcl_DStringValue(&ds))) { + if (userEncoding) { + Tcl_FreeEncoding(userEncoding); + } + userEncoding = Tcl_GetEncoding(interp, Tcl_DStringValue(&ds)); + } + Tcl_DStringFree(&ds); + return userEncoding; +} + const char * Tcl_GetEncodingNameFromEnvironment( Tcl_DString *bufPtr) -- cgit v0.12 From d1d3fd976dd58be3a48e928517580b4f11206fe2 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 25 Apr 2025 08:11:10 +0000 Subject: Protect TclWinGetUserEncoding() for environments it doen't exist, e.g. Tcl 9.0.0/9.0.1 --- generic/tclIntPlatDecls.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/generic/tclIntPlatDecls.h b/generic/tclIntPlatDecls.h index ebc5896..527536e 100644 --- a/generic/tclIntPlatDecls.h +++ b/generic/tclIntPlatDecls.h @@ -738,4 +738,12 @@ MODULE_SCOPE int TclMacOSXMatchType(Tcl_Interp *interp, # define TclpGetPid(pid) ((Tcl_Size)(pid)) #endif +#ifdef USE_TCL_STUBS +/* Protect TclWinGetUserEncoding() for environments it doen't exist, e.g. Tcl 9.0.0/9.0.1 */ +#undef TclWinGetUserEncoding +#define TclWinGetUserEncoding(interp) \ + (tclIntPlatStubsPtr->tclWinGetUserEncoding ? tclIntPlatStubsPtr->tclWinGetUserEncoding(interp) : NULL) +#endif + + #endif /* _TCLINTPLATDECLS */ -- cgit v0.12