| | @@ -32,17 +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 |
| 38 | | -#endif |
| 39 | | - |
| 40 | | -#ifdef _WIN32 |
| 41 | | -# include <windows.h> |
| 42 | | -#else |
| 43 | | -# include <dlfcn.h> |
| 37 | +# define USE_TCL_EVALOBJV 1 |
| 44 | 38 | #endif |
| 45 | 39 | |
| 46 | 40 | /* |
| 47 | 41 | ** These macros are designed to reduce the redundant code required to marshal |
| 48 | 42 | ** arguments from TH1 to Tcl. |
| | @@ -71,10 +65,76 @@ |
| 71 | 65 | ** context. |
| 72 | 66 | */ |
| 73 | 67 | #define GET_CTX_TCL_INTERP(ctx) \ |
| 74 | 68 | ((struct TclContext *)(ctx))->interp |
| 75 | 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 | + |
| 76 | 136 | /* |
| 77 | 137 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 |
| 78 | 138 | ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied |
| 79 | 139 | ** by the caller. This must be declared here because quite a few functions in |
| 80 | 140 | ** this file need to use it before it can be defined. |
| | @@ -108,10 +168,13 @@ |
| 108 | 168 | ** copied from and should be kept in sync with the one in "main.c". |
| 109 | 169 | */ |
| 110 | 170 | struct TclContext { |
| 111 | 171 | int argc; |
| 112 | 172 | char **argv; |
| 173 | + void *library; |
| 174 | + tcl_FindExecutableProc *xFindExecutable; |
| 175 | + tcl_CreateInterpProc *xCreateInterp; |
| 113 | 176 | Tcl_Interp *interp; |
| 114 | 177 | }; |
| 115 | 178 | |
| 116 | 179 | /* |
| 117 | 180 | ** Syntax: |
| | @@ -381,10 +444,77 @@ |
| 381 | 444 | /* Remove the Tcl integration commands. */ |
| 382 | 445 | for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){ |
| 383 | 446 | Th_RenameCommand(th1Interp, aCommand[i].zName, -1, NULL, 0); |
| 384 | 447 | } |
| 385 | 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 | +} |
| 386 | 516 | |
| 387 | 517 | /* |
| 388 | 518 | ** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter |
| 389 | 519 | ** based on the supplied command line arguments. |
| 390 | 520 | */ |
| | @@ -451,70 +581,31 @@ |
| 451 | 581 | ){ |
| 452 | 582 | struct TclContext *tclContext = (struct TclContext *)pContext; |
| 453 | 583 | int argc; |
| 454 | 584 | char **argv; |
| 455 | 585 | char *argv0 = 0; |
| 456 | | -#ifdef USE_TCL_STUBS |
| 457 | | -#ifdef _WIN32 |
| 458 | | - WCHAR lib[] = L"tcl87.dll"; |
| 459 | | -#define minver lib[4] |
| 460 | | -#define dlopen(a,b) (void *)LoadLibraryW(a); |
| 461 | | -#define dlsym(a,b) GetProcAddress((HANDLE)(a),b); |
| 462 | | -#else |
| 463 | | -#ifdef __CYGWIN__ |
| 464 | | - char lib[] = "libtcl8.7.dll"; |
| 465 | | -#else |
| 466 | | - char lib[] = "libtcl8.7.so"; |
| 467 | | -#endif |
| 468 | | -#define minver lib[8] |
| 469 | | -#endif |
| 470 | | - void *handle = NULL; |
| 471 | | - void (*findExecutable)(const char *) = 0; |
| 472 | | - Tcl_Interp *(*createInterp)() = 0; |
| 473 | | -#endif /* USE_TCL_STUBS */ |
| 474 | 586 | Tcl_Interp *tclInterp; |
| 475 | 587 | |
| 476 | 588 | if ( !tclContext ){ |
| 477 | 589 | Th_ErrorMessage(interp, |
| 478 | 590 | "Invalid Tcl context", (const char *)"", 0); |
| 479 | 591 | return TH_ERROR; |
| 480 | 592 | } |
| 481 | 593 | if ( tclContext->interp ){ |
| 482 | 594 | return TH_OK; |
| 595 | + } |
| 596 | + if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable, |
| 597 | + &tclContext->xCreateInterp)!=TH_OK ){ |
| 598 | + return TH_ERROR; |
| 483 | 599 | } |
| 484 | 600 | argc = tclContext->argc; |
| 485 | 601 | argv = tclContext->argv; |
| 486 | 602 | if( argc>0 && argv ){ |
| 487 | 603 | argv0 = argv[0]; |
| 488 | 604 | } |
| 489 | | -#ifdef USE_TCL_STUBS |
| 490 | | - while( --minver>'3' ){ |
| 491 | | - handle = dlopen(lib, RTLD_NOW | RTLD_LOCAL); |
| 492 | | - if( handle ) { |
| 493 | | - const char *sym = "_Tcl_FindExecutable"; |
| 494 | | - findExecutable = (void (*)(const char *)) dlsym(handle, sym+1); |
| 495 | | - if (!findExecutable) |
| 496 | | - findExecutable = (void (*)(const char *)) dlsym(handle, sym); |
| 497 | | - sym = "_Tcl_CreateInterp"; |
| 498 | | - createInterp = (Tcl_Interp * (*)(void)) dlsym(handle, sym+1); |
| 499 | | - if (!createInterp) |
| 500 | | - createInterp = (Tcl_Interp * (*)(void)) dlsym(handle, sym); |
| 501 | | - break; |
| 502 | | - } |
| 503 | | - } |
| 504 | | - if( !handle ){ |
| 505 | | - Th_ErrorMessage(interp, |
| 506 | | - "Could not create Tcl interpreter", (const char *)"", 0); |
| 507 | | - return TH_ERROR; |
| 508 | | - } |
| 509 | | -# undef Tcl_FindExecutable |
| 510 | | -# define Tcl_FindExecutable findExecutable |
| 511 | | -# undef Tcl_CreateInterp |
| 512 | | -# define Tcl_CreateInterp createInterp |
| 513 | | -#endif /* USE_TCL_STUBS */ |
| 514 | | - Tcl_FindExecutable(argv0); |
| 515 | | - tclInterp = Tcl_CreateInterp(); |
| 605 | + tclContext->xFindExecutable(argv0); |
| 606 | + tclInterp = tclContext->xCreateInterp(); |
| 516 | 607 | if( !tclInterp || !Tcl_InitStubs(tclInterp, "8.4", 0) |
| 517 | 608 | || Tcl_InterpDeleted(tclInterp) ){ |
| 518 | 609 | Th_ErrorMessage(interp, |
| 519 | 610 | "Could not create Tcl interpreter", (const char *)"", 0); |
| 520 | 611 | return TH_ERROR; |
| 521 | 612 | |