| | @@ -32,11 +32,11 @@ |
| 32 | 32 | ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6)) |
| 33 | 33 | /* |
| 34 | 34 | ** Workaround NRE-specific issue in Tcl_EvalObjCmd (SF bug #3399564) by using |
| 35 | 35 | ** Tcl_EvalObjv instead of invoking the objProc directly. |
| 36 | 36 | */ |
| 37 | | -#define USE_TCL_EVALOBJV 1 |
| 37 | +# define USE_TCL_EVALOBJV 1 |
| 38 | 38 | #endif |
| 39 | 39 | |
| 40 | 40 | /* |
| 41 | 41 | ** These macros are designed to reduce the redundant code required to marshal |
| 42 | 42 | ** arguments from TH1 to Tcl. |
| | @@ -65,10 +65,76 @@ |
| 65 | 65 | ** context. |
| 66 | 66 | */ |
| 67 | 67 | #define GET_CTX_TCL_INTERP(ctx) \ |
| 68 | 68 | ((struct TclContext *)(ctx))->interp |
| 69 | 69 | |
| 70 | +/* |
| 71 | +** Define the Tcl shared library name, some exported function names, and some |
| 72 | +** cross-platform macros for use with the Tcl stubs mechanism, when enabled. |
| 73 | + */ |
| 74 | +#if defined(USE_TCL_STUBS) |
| 75 | +# if defined(_WIN32) |
| 76 | +# define WIN32_LEAN_AND_MEAN |
| 77 | +# include <windows.h> |
| 78 | +# ifndef TCL_LIBRARY_NAME |
| 79 | +# define TCL_LIBRARY_NAME "tcl86.dll\0" |
| 80 | +# endif |
| 81 | +# ifndef TCL_MINOR_OFFSET |
| 82 | +# define TCL_MINOR_OFFSET (4) |
| 83 | +# endif |
| 84 | +# ifndef dlopen |
| 85 | +# define dlopen(a,b) (void *)LoadLibrary((a)); |
| 86 | +# endif |
| 87 | +# ifndef dlsym |
| 88 | +# define dlsym(a,b) GetProcAddress((HANDLE)(a),(b)); |
| 89 | +# endif |
| 90 | +# ifndef dlclose |
| 91 | +# define dlclose(a) FreeLibrary((HANDLE)(a)); |
| 92 | +# endif |
| 93 | +# else |
| 94 | +# include <dlfcn.h> |
| 95 | +# if defined(__CYGWIN__) |
| 96 | +# ifndef TCL_LIBRARY_NAME |
| 97 | +# define TCL_LIBRARY_NAME "libtcl8.6.dll\0" |
| 98 | +# endif |
| 99 | +# ifndef TCL_MINOR_OFFSET |
| 100 | +# define TCL_MINOR_OFFSET (8) |
| 101 | +# endif |
| 102 | +# elif defined(__APPLE__) |
| 103 | +# ifndef TCL_LIBRARY_NAME |
| 104 | +# define TCL_LIBRARY_NAME "libtcl8.6.dylib\0" |
| 105 | +# endif |
| 106 | +# ifndef TCL_MINOR_OFFSET |
| 107 | +# define TCL_MINOR_OFFSET (8) |
| 108 | +# endif |
| 109 | +# else |
| 110 | +# ifndef TCL_LIBRARY_NAME |
| 111 | +# define TCL_LIBRARY_NAME "libtcl8.6.so\0" |
| 112 | +# endif |
| 113 | +# ifndef TCL_MINOR_OFFSET |
| 114 | +# define TCL_MINOR_OFFSET (8) |
| 115 | +# endif |
| 116 | +# endif /* defined(__CYGWIN__) */ |
| 117 | +# endif /* defined(_WIN32) */ |
| 118 | +# ifndef TCL_FINDEXECUTABLE_NAME |
| 119 | +# define TCL_FINDEXECUTABLE_NAME "_Tcl_FindExecutable" |
| 120 | +# endif |
| 121 | +# ifndef TCL_CREATEINTERP_NAME |
| 122 | +# define TCL_CREATEINTERP_NAME "_Tcl_CreateInterp" |
| 123 | +# endif |
| 124 | +#endif /* defined(USE_TCL_STUBS) */ |
| 125 | + |
| 126 | +/* |
| 127 | +** The function pointer types for Tcl_FindExecutable and Tcl_CreateInterp are |
| 128 | +** needed when the Tcl library is being loaded dynamically by a stubs-enabled |
| 129 | +** application (i.e. the inverse of using a stubs-enabled package). These are |
| 130 | +** the only Tcl API functions that MUST be called prior to being able to call |
| 131 | +** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). |
| 132 | + */ |
| 133 | +typedef void (tcl_FindExecutableProc) (CONST char * argv0); |
| 134 | +typedef Tcl_Interp *(tcl_CreateInterpProc) (void); |
| 135 | + |
| 70 | 136 | /* |
| 71 | 137 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 |
| 72 | 138 | ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied |
| 73 | 139 | ** by the caller. This must be declared here because quite a few functions in |
| 74 | 140 | ** this file need to use it before it can be defined. |
| | @@ -102,10 +168,13 @@ |
| 102 | 168 | ** copied from and should be kept in sync with the one in "main.c". |
| 103 | 169 | */ |
| 104 | 170 | struct TclContext { |
| 105 | 171 | int argc; |
| 106 | 172 | char **argv; |
| 173 | + void *library; |
| 174 | + tcl_FindExecutableProc *xFindExecutable; |
| 175 | + tcl_CreateInterpProc *xCreateInterp; |
| 107 | 176 | Tcl_Interp *interp; |
| 108 | 177 | }; |
| 109 | 178 | |
| 110 | 179 | /* |
| 111 | 180 | ** Syntax: |
| | @@ -224,18 +293,18 @@ |
| 224 | 293 | int argc, |
| 225 | 294 | const char **argv, |
| 226 | 295 | int *argl |
| 227 | 296 | ){ |
| 228 | 297 | Tcl_Interp *tclInterp; |
| 229 | | -#ifndef USE_TCL_EVALOBJV |
| 298 | +#if !defined(USE_TCL_EVALOBJV) |
| 230 | 299 | Tcl_Command command; |
| 231 | 300 | Tcl_CmdInfo cmdInfo; |
| 232 | 301 | #endif |
| 233 | 302 | int rc; |
| 234 | 303 | int nResult; |
| 235 | 304 | const char *zResult; |
| 236 | | -#ifndef USE_TCL_EVALOBJV |
| 305 | +#if !defined(USE_TCL_EVALOBJV) |
| 237 | 306 | Tcl_Obj *objPtr; |
| 238 | 307 | #endif |
| 239 | 308 | USE_ARGV_TO_OBJV(); |
| 240 | 309 | |
| 241 | 310 | if ( createTclInterp(interp, ctx)!=TH_OK ){ |
| | @@ -248,11 +317,11 @@ |
| 248 | 317 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 249 | 318 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 250 | 319 | return TH_ERROR; |
| 251 | 320 | } |
| 252 | 321 | Tcl_Preserve((ClientData)tclInterp); |
| 253 | | -#ifndef USE_TCL_EVALOBJV |
| 322 | +#if !defined(USE_TCL_EVALOBJV) |
| 254 | 323 | objPtr = Tcl_NewStringObj(argv[1], argl[1]); |
| 255 | 324 | Tcl_IncrRefCount(objPtr); |
| 256 | 325 | command = Tcl_GetCommandFromObj(tclInterp, objPtr); |
| 257 | 326 | if( !command || Tcl_GetCommandInfoFromToken(command,&cmdInfo)==0 ){ |
| 258 | 327 | Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]); |
| | @@ -267,11 +336,11 @@ |
| 267 | 336 | return TH_ERROR; |
| 268 | 337 | } |
| 269 | 338 | Tcl_DecrRefCount(objPtr); |
| 270 | 339 | #endif |
| 271 | 340 | COPY_ARGV_TO_OBJV(); |
| 272 | | -#ifdef USE_TCL_EVALOBJV |
| 341 | +#if defined(USE_TCL_EVALOBJV) |
| 273 | 342 | rc = Tcl_EvalObjv(tclInterp, objc, objv, 0); |
| 274 | 343 | #else |
| 275 | 344 | Tcl_ResetResult(tclInterp); |
| 276 | 345 | rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv); |
| 277 | 346 | #endif |
| | @@ -375,10 +444,77 @@ |
| 375 | 444 | /* Remove the Tcl integration commands. */ |
| 376 | 445 | for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){ |
| 377 | 446 | Th_RenameCommand(th1Interp, aCommand[i].zName, -1, NULL, 0); |
| 378 | 447 | } |
| 379 | 448 | } |
| 449 | + |
| 450 | +/* |
| 451 | +** When Tcl stubs support is enabled, attempts to dynamically load the Tcl |
| 452 | +** shared library and fetch the function pointers necessary to create an |
| 453 | +** interpreter and initialize the stubs mechanism; otherwise, simply setup |
| 454 | +** the function pointers provided by the caller with the statically linked |
| 455 | +** functions. |
| 456 | + */ |
| 457 | +static int loadTcl( |
| 458 | + Th_Interp *interp, |
| 459 | + void **pLibrary, |
| 460 | + tcl_FindExecutableProc **pxFindExecutable, |
| 461 | + tcl_CreateInterpProc **pxCreateInterp |
| 462 | +){ |
| 463 | +#if defined(USE_TCL_STUBS) |
| 464 | + char fileName[] = TCL_LIBRARY_NAME; |
| 465 | +#endif |
| 466 | + if( !pLibrary || !pxFindExecutable || !pxCreateInterp ){ |
| 467 | + Th_ErrorMessage(interp, |
| 468 | + "Invalid Tcl loader argument(s)", (const char *)"", 0); |
| 469 | + return TH_ERROR; |
| 470 | + } |
| 471 | +#if defined(USE_TCL_STUBS) |
| 472 | + do { |
| 473 | + void *library = dlopen(fileName, RTLD_NOW | RTLD_GLOBAL); |
| 474 | + if( library ){ |
| 475 | + tcl_FindExecutableProc *xFindExecutable; |
| 476 | + tcl_CreateInterpProc *xCreateInterp; |
| 477 | + const char *procName = TCL_FINDEXECUTABLE_NAME; |
| 478 | + xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName + 1); |
| 479 | + if( !xFindExecutable ){ |
| 480 | + xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName); |
| 481 | + } |
| 482 | + if( !xFindExecutable ){ |
| 483 | + Th_ErrorMessage(interp, |
| 484 | + "Could not locate Tcl_FindExecutable", (const char *)"", 0); |
| 485 | + dlclose(library); |
| 486 | + return TH_ERROR; |
| 487 | + } |
| 488 | + procName = TCL_CREATEINTERP_NAME; |
| 489 | + xCreateInterp = (tcl_CreateInterpProc *)dlsym(library, procName + 1); |
| 490 | + if( !xCreateInterp ){ |
| 491 | + xCreateInterp = (tcl_CreateInterpProc *)dlsym(library, procName); |
| 492 | + } |
| 493 | + if( !xCreateInterp ){ |
| 494 | + Th_ErrorMessage(interp, |
| 495 | + "Could not locate Tcl_CreateInterp", (const char *)"", 0); |
| 496 | + dlclose(library); |
| 497 | + return TH_ERROR; |
| 498 | + } |
| 499 | + *pLibrary = library; |
| 500 | + *pxFindExecutable = xFindExecutable; |
| 501 | + *pxCreateInterp = xCreateInterp; |
| 502 | + return TH_OK; |
| 503 | + } |
| 504 | + } while( --fileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */ |
| 505 | + Th_ErrorMessage(interp, |
| 506 | + "Could not load Tcl shared library \"" TCL_LIBRARY_NAME "\"", |
| 507 | + (const char *)"", 0); |
| 508 | + return TH_ERROR; |
| 509 | +#else |
| 510 | + *pLibrary = 0; |
| 511 | + *pxFindExecutable = Tcl_FindExecutable; |
| 512 | + *pxCreateInterp = Tcl_CreateInterp; |
| 513 | + return TH_OK; |
| 514 | +#endif |
| 515 | +} |
| 380 | 516 | |
| 381 | 517 | /* |
| 382 | 518 | ** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter |
| 383 | 519 | ** based on the supplied command line arguments. |
| 384 | 520 | */ |
| | @@ -454,23 +590,32 @@ |
| 454 | 590 | "Invalid Tcl context", (const char *)"", 0); |
| 455 | 591 | return TH_ERROR; |
| 456 | 592 | } |
| 457 | 593 | if ( tclContext->interp ){ |
| 458 | 594 | return TH_OK; |
| 595 | + } |
| 596 | + if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable, |
| 597 | + &tclContext->xCreateInterp)!=TH_OK ){ |
| 598 | + return TH_ERROR; |
| 459 | 599 | } |
| 460 | 600 | argc = tclContext->argc; |
| 461 | 601 | argv = tclContext->argv; |
| 462 | 602 | if( argc>0 && argv ){ |
| 463 | 603 | argv0 = argv[0]; |
| 464 | 604 | } |
| 465 | | - Tcl_FindExecutable(argv0); |
| 466 | | - tclInterp = tclContext->interp = Tcl_CreateInterp(); |
| 467 | | - if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 605 | + tclContext->xFindExecutable(argv0); |
| 606 | + tclInterp = tclContext->xCreateInterp(); |
| 607 | + if( !tclInterp || |
| 608 | +#if defined(USE_TCL_STUBS) |
| 609 | + !Tcl_InitStubs(tclInterp, "8.4", 0) || |
| 610 | +#endif |
| 611 | + Tcl_InterpDeleted(tclInterp) ){ |
| 468 | 612 | Th_ErrorMessage(interp, |
| 469 | 613 | "Could not create Tcl interpreter", (const char *)"", 0); |
| 470 | 614 | return TH_ERROR; |
| 471 | 615 | } |
| 616 | + tclContext->interp = tclInterp; |
| 472 | 617 | if( Tcl_Init(tclInterp)!=TCL_OK ){ |
| 473 | 618 | Th_ErrorMessage(interp, |
| 474 | 619 | "Tcl initialization error:", Tcl_GetStringResult(tclInterp), -1); |
| 475 | 620 | Tcl_DeleteInterp(tclInterp); |
| 476 | 621 | tclContext->interp = tclInterp = 0; |
| 477 | 622 | |