| | @@ -119,29 +119,27 @@ |
| 119 | 119 | # define TCL_FINDEXECUTABLE_NAME "_Tcl_FindExecutable" |
| 120 | 120 | # endif |
| 121 | 121 | # ifndef TCL_CREATEINTERP_NAME |
| 122 | 122 | # define TCL_CREATEINTERP_NAME "_Tcl_CreateInterp" |
| 123 | 123 | # endif |
| 124 | | -#define tclStubsPtr staticTclStubsPtr |
| 125 | | -static const TclStubs *tclStubsPtr = NULL; |
| 126 | | -typedef struct { |
| 127 | | - char *notused1; |
| 128 | | - Tcl_FreeProc *notused2; |
| 129 | | - int notused3; |
| 130 | | - const struct TclStubs *stubTable; |
| 131 | | -} Interp; |
| 124 | +# ifndef TCL_DELETEINTERP_NAME |
| 125 | +# define TCL_DELETEINTERP_NAME "_Tcl_DeleteInterp" |
| 126 | +# endif |
| 132 | 127 | #endif /* defined(USE_TCL_STUBS) */ |
| 133 | 128 | |
| 134 | 129 | /* |
| 135 | 130 | ** The function types for Tcl_FindExecutable and Tcl_CreateInterp are needed |
| 136 | 131 | ** when the Tcl library is being loaded dynamically by a stubs-enabled |
| 137 | 132 | ** application (i.e. the inverse of using a stubs-enabled package). These are |
| 138 | 133 | ** the only Tcl API functions that MUST be called prior to being able to call |
| 139 | | -** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). |
| 134 | +** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete |
| 135 | +** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp |
| 136 | +** function type is also required. |
| 140 | 137 | */ |
| 141 | 138 | typedef void (tcl_FindExecutableProc) (const char * argv0); |
| 142 | 139 | typedef Tcl_Interp *(tcl_CreateInterpProc) (void); |
| 140 | +typedef void (tcl_DeleteInterpProc) (Tcl_Interp *interp); |
| 143 | 141 | |
| 144 | 142 | /* |
| 145 | 143 | ** The function types for the "hook" functions to be called before and after a |
| 146 | 144 | ** TH1 command makes a call to evaluate a Tcl script. If the "pre" function |
| 147 | 145 | ** returns anything but TH_OK, then evaluation of the Tcl script is skipped and |
| | @@ -157,10 +155,65 @@ |
| 157 | 155 | const char **argv, /* Array of arguments for the TH1 command. */ |
| 158 | 156 | int *argl, /* Array of lengths for the TH1 command arguments. */ |
| 159 | 157 | int rc /* Recommended notification return value. */ |
| 160 | 158 | ); |
| 161 | 159 | |
| 160 | +/* |
| 161 | +** Are we using our own private implementation of the Tcl stubs mechanism? If |
| 162 | +** this is enabled, it prevents the user from having to link against the Tcl |
| 163 | +** stubs library for the target platform, which may not be readily available. |
| 164 | + */ |
| 165 | +#if defined(FOSSIL_ENABLE_TCL_FAKE_STUBS) |
| 166 | +/* |
| 167 | +** HACK: Using some preprocessor magic and a private static variable, redirect |
| 168 | +** the Tcl API calls [found within this file] to the function pointers |
| 169 | +** that will be contained in our private Tcl stubs table. This takes |
| 170 | +** advantage of the fact that the Tcl headers always define the Tcl API |
| 171 | +** functions in terms of the "tclStubsPtr" variable. |
| 172 | + */ |
| 173 | +#define tclStubsPtr privateTclStubsPtr |
| 174 | +static const TclStubs *tclStubsPtr = NULL; |
| 175 | + |
| 176 | +/* |
| 177 | +** Create a Tcl interpreter structure that mirrors just enough fields to get |
| 178 | +** it up and running successfully with our private implementation of the Tcl |
| 179 | +** stubs mechanism. |
| 180 | + */ |
| 181 | +struct PrivateTclInterp { |
| 182 | + char *result; |
| 183 | + Tcl_FreeProc *freeProc; |
| 184 | + int errorLine; |
| 185 | + const struct TclStubs *stubTable; |
| 186 | +}; |
| 187 | + |
| 188 | +/* |
| 189 | +** Fossil can now be compiled without linking to the actual Tcl stubs library. |
| 190 | +** In that case, this function will be used to perform those steps that would |
| 191 | +** normally be performed within the Tcl stubs library. |
| 192 | + */ |
| 193 | +static int initTclStubs( |
| 194 | + Th_Interp *interp, |
| 195 | + Tcl_Interp *tclInterp |
| 196 | +){ |
| 197 | + tclStubsPtr = ((struct PrivateTclInterp *)tclInterp)->stubTable; |
| 198 | + if( !tclStubsPtr || (tclStubsPtr->magic!=TCL_STUB_MAGIC) ){ |
| 199 | + Th_ErrorMessage(interp, |
| 200 | + "could not initialize Tcl stubs: incompatible mechanism", |
| 201 | + (const char *)"", 0); |
| 202 | + return TH_ERROR; |
| 203 | + } |
| 204 | + /* NOTE: At this point, the Tcl API functions should be available. */ |
| 205 | + if( Tcl_PkgRequireEx(tclInterp, "Tcl", "8.4", 0, (void *)&tclStubsPtr)==0 ){ |
| 206 | + Th_ErrorMessage(interp, |
| 207 | + "could not create Tcl interpreter: incompatible version", |
| 208 | + (const char *)"", 0); |
| 209 | + return TH_ERROR; |
| 210 | + } |
| 211 | + return TH_OK; |
| 212 | +} |
| 213 | +#endif |
| 214 | + |
| 162 | 215 | /* |
| 163 | 216 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 |
| 164 | 217 | ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied |
| 165 | 218 | ** by the caller. This must be declared here because quite a few functions in |
| 166 | 219 | ** this file need to use it before it can be defined. |
| | @@ -197,10 +250,11 @@ |
| 197 | 250 | int argc; /* Number of original arguments. */ |
| 198 | 251 | char **argv; /* Full copy of the original arguments. */ |
| 199 | 252 | void *library; /* The Tcl library module handle. */ |
| 200 | 253 | tcl_FindExecutableProc *xFindExecutable; /* Tcl_FindExecutable() pointer. */ |
| 201 | 254 | tcl_CreateInterpProc *xCreateInterp; /* Tcl_CreateInterp() pointer. */ |
| 255 | + tcl_DeleteInterpProc *xDeleteInterp; /* Tcl_DeleteInterp() pointer. */ |
| 202 | 256 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 203 | 257 | char *setup; /* The optional Tcl setup script. */ |
| 204 | 258 | tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 205 | 259 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 206 | 260 | tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| | @@ -534,11 +588,12 @@ |
| 534 | 588 | */ |
| 535 | 589 | static int loadTcl( |
| 536 | 590 | Th_Interp *interp, |
| 537 | 591 | void **pLibrary, |
| 538 | 592 | tcl_FindExecutableProc **pxFindExecutable, |
| 539 | | - tcl_CreateInterpProc **pxCreateInterp |
| 593 | + tcl_CreateInterpProc **pxCreateInterp, |
| 594 | + tcl_DeleteInterpProc **pxDeleteInterp |
| 540 | 595 | ){ |
| 541 | 596 | #if defined(USE_TCL_STUBS) |
| 542 | 597 | char fileName[] = TCL_LIBRARY_NAME; |
| 543 | 598 | #endif |
| 544 | 599 | if( !pLibrary || !pxFindExecutable || !pxCreateInterp ){ |
| | @@ -550,10 +605,11 @@ |
| 550 | 605 | do { |
| 551 | 606 | void *library = dlopen(fileName, RTLD_NOW | RTLD_GLOBAL); |
| 552 | 607 | if( library ){ |
| 553 | 608 | tcl_FindExecutableProc *xFindExecutable; |
| 554 | 609 | tcl_CreateInterpProc *xCreateInterp; |
| 610 | + tcl_DeleteInterpProc *xDeleteInterp; |
| 555 | 611 | const char *procName = TCL_FINDEXECUTABLE_NAME; |
| 556 | 612 | xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName + 1); |
| 557 | 613 | if( !xFindExecutable ){ |
| 558 | 614 | xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName); |
| 559 | 615 | } |
| | @@ -571,14 +627,26 @@ |
| 571 | 627 | if( !xCreateInterp ){ |
| 572 | 628 | Th_ErrorMessage(interp, |
| 573 | 629 | "could not locate Tcl_CreateInterp", (const char *)"", 0); |
| 574 | 630 | dlclose(library); |
| 575 | 631 | return TH_ERROR; |
| 632 | + } |
| 633 | + procName = TCL_DELETEINTERP_NAME; |
| 634 | + xDeleteInterp = (tcl_DeleteInterpProc *)dlsym(library, procName + 1); |
| 635 | + if( !xDeleteInterp ){ |
| 636 | + xDeleteInterp = (tcl_DeleteInterpProc *)dlsym(library, procName); |
| 637 | + } |
| 638 | + if( !xDeleteInterp ){ |
| 639 | + Th_ErrorMessage(interp, |
| 640 | + "could not locate Tcl_DeleteInterp", (const char *)"", 0); |
| 641 | + dlclose(library); |
| 642 | + return TH_ERROR; |
| 576 | 643 | } |
| 577 | 644 | *pLibrary = library; |
| 578 | 645 | *pxFindExecutable = xFindExecutable; |
| 579 | 646 | *pxCreateInterp = xCreateInterp; |
| 647 | + *pxDeleteInterp = xDeleteInterp; |
| 580 | 648 | return TH_OK; |
| 581 | 649 | } |
| 582 | 650 | } while( --fileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */ |
| 583 | 651 | Th_ErrorMessage(interp, |
| 584 | 652 | "could not load Tcl shared library \"" TCL_LIBRARY_NAME "\"", |
| | @@ -671,43 +739,45 @@ |
| 671 | 739 | } |
| 672 | 740 | if ( tclContext->interp ){ |
| 673 | 741 | return TH_OK; |
| 674 | 742 | } |
| 675 | 743 | if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable, |
| 676 | | - &tclContext->xCreateInterp)!=TH_OK ){ |
| 744 | + &tclContext->xCreateInterp, &tclContext->xDeleteInterp)!=TH_OK ){ |
| 677 | 745 | return TH_ERROR; |
| 678 | 746 | } |
| 679 | 747 | argc = tclContext->argc; |
| 680 | 748 | argv = tclContext->argv; |
| 681 | 749 | if( argc>0 && argv ){ |
| 682 | 750 | argv0 = argv[0]; |
| 683 | 751 | } |
| 684 | 752 | tclContext->xFindExecutable(argv0); |
| 685 | 753 | tclInterp = tclContext->xCreateInterp(); |
| 686 | | - |
| 687 | | -#if defined(USE_TCL_STUBS) |
| 688 | | - if( tclInterp ){ |
| 689 | | - tclStubsPtr = ((Interp *) tclInterp)->stubTable; |
| 690 | | - if (!tclStubsPtr || (tclStubsPtr->magic != TCL_STUB_MAGIC)) { |
| 691 | | - Th_ErrorMessage(interp, |
| 692 | | - "could not create Tcl interpreter: " |
| 693 | | - "incompatible stubs mechanism", (const char *)"", 0); |
| 694 | | - return TH_ERROR; |
| 695 | | - } |
| 696 | | - if( Tcl_PkgRequireEx(tclInterp, "Tcl", "8.4", 0, (void *)&tclStubsPtr)==0 ){ |
| 697 | | - Th_ErrorMessage(interp, |
| 698 | | - "could not create Tcl interpreter: " |
| 699 | | - "incompatible version", (const char *)"", 0); |
| 700 | | - return TH_ERROR; |
| 701 | | - } |
| 702 | | - } |
| 703 | | -#endif |
| 704 | | - if( !tclInterp || |
| 705 | | - Tcl_InterpDeleted(tclInterp) ){ |
| 754 | + if( !tclInterp ){ |
| 706 | 755 | Th_ErrorMessage(interp, |
| 707 | 756 | "could not create Tcl interpreter", (const char *)"", 0); |
| 708 | 757 | return TH_ERROR; |
| 758 | + } |
| 759 | +#if defined(USE_TCL_STUBS) |
| 760 | +#if defined(FOSSIL_ENABLE_TCL_FAKE_STUBS) |
| 761 | + if( initTclStubs(interp, tclInterp)!=TH_OK ){ |
| 762 | + tclContext->xDeleteInterp(tclInterp); |
| 763 | + return TH_ERROR; |
| 764 | + } |
| 765 | +#else |
| 766 | + if( !Tcl_InitStubs(tclInterp, "8.4", 0) ){ |
| 767 | + Th_ErrorMessage(interp, |
| 768 | + "could not initialize Tcl stubs", (const char *)"", 0); |
| 769 | + tclContext->xDeleteInterp(tclInterp); |
| 770 | + return TH_ERROR; |
| 771 | + } |
| 772 | +#endif /* defined(FOSSIL_ENABLE_TCL_FAKE_STUBS) */ |
| 773 | +#endif /* defined(USE_TCL_STUBS) */ |
| 774 | + if( Tcl_InterpDeleted(tclInterp) ){ |
| 775 | + Th_ErrorMessage(interp, |
| 776 | + "Tcl interpreter appears to be deleted", (const char *)"", 0); |
| 777 | + tclContext->xDeleteInterp(tclInterp); /* TODO: Redundant? */ |
| 778 | + return TH_ERROR; |
| 709 | 779 | } |
| 710 | 780 | tclContext->interp = tclInterp; |
| 711 | 781 | if( Tcl_Init(tclInterp)!=TCL_OK ){ |
| 712 | 782 | Th_ErrorMessage(interp, |
| 713 | 783 | "Tcl initialization error:", Tcl_GetStringResult(tclInterp), -1); |
| 714 | 784 | |