Subject: Request Tcl 8.2b2 dynamic loading improvement for tclsh on Win32 - DN [1]


davygrvy@bigfoot.com - 09 Aug 1999 - comp.lang.tcl

 Tcl 8.2b2 Request:  Generated by Scriptics' bug entry form at
     http://www.scriptics.com/support/bugForm.html
 Responses to this post are encouraged.
 ------

 Submitted by:  David Gravereaux
 OperatingSystem:  Windows NT
 OperatingSystemVersion:  4.0 sp5
 Synopsis:  dynamic loading improvement for tclsh on Win32

 DesiredBehavior:
     This patch gets tclsh using Stubs and adds a dynamic loading mechanism.

     It only works on Win32 at the moment, but could be improved with
     additional dlopen() function that Jan Nijtmans has.

     The search routine (Stub_LoadTcl) will need some inspection to determine
     if it's optimal.  I like it though.

 Patch:
 Index: tcl.h
 ===================================================================
 RCS file: /cvsroot/tcl/generic/tcl.h,v
 retrieving revision 1.56
 diff -c -r1.56 tcl.h
 *** tcl.h    1999/08/02 17:45:36    1.56
 --- tcl.h    1999/08/09 09:25:02
 ***************
 *** 1579,1584 ****
 --- 1579,1591 ----

   EXTERN char *        Tcl_InitStubs _ANSI_ARGS_((Tcl_Interp *interp,
                   char *version, int exact));
 + EXTERN char *        Stub_LoadTcl _ANSI_ARGS_((char *minVer, int exact,
 +                 int dbgOnly));
 + EXTERN VOID        Stub_PreInit _ANSI_ARGS_((char *ver, int exact, char *app));
 + EXTERN VOID *        Stub_GetMain _ANSI_ARGS_(());
 + EXTERN VOID *        Stub_GetCreateInterp _ANSI_ARGS_(());
 + EXTERN char *        Stub_LoadTclError _ANSI_ARGS_(());
 +

   #ifndef USE_TCL_STUBS

 Index: tclAppInit.c
 ===================================================================
 RCS file: /cvsroot/tcl/win/tclAppInit.c,v
 retrieving revision 1.5
 diff -c -r1.5 tclAppInit.c
 *** tclAppInit.c    1999/04/16 00:48:07    1.5
 --- tclAppInit.c    1999/08/09 19:13:18
 ***************
 *** 53,58 ****
 --- 53,70 ----
       int argc;            /* Number of command-line arguments. */
       char **argv;        /* Values of command-line arguments. */
   {
 + #ifdef USE_TCL_STUBS
 +     void *(*tclMainProc) _ANSI_ARGS_((int argc, char **argv,
 +     Tcl_AppInitProc *appInitProc));
 +
 + #ifdef DEBUG
 +     Stub_LoadTcl(TCL_VERSION, /*exact*/1, /*debug*/1);
 + #else
 +     Stub_LoadTcl(TCL_VERSION, /*exact*/1, /*debug*/0);
 + #endif
 +     tclMainProc = Stub_GetMain();
 + #endif
 +
       /*
        * Set up the default locale to be standard "C" locale so parsing
        * is performed correctly.
 ***************
 *** 61,67 ****
 --- 73,84 ----
       setlocale(LC_ALL, "C");
       setargv(&argc, &argv);

 +
 + #ifdef USE_TCL_STUBS
 +     tclMainProc(argc, argv, Tcl_AppInit);
 + #else
       Tcl_Main(argc, argv, Tcl_AppInit);
 + #endif
       return 0;            /* Needed only to prevent compiler warning. */
   }

 ***************
 *** 89,98 ****
   Tcl_AppInit(interp)
       Tcl_Interp *interp;        /* Interpreter for application. */
   {
       if (Tcl_Init(interp) == TCL_ERROR) {
       return TCL_ERROR;
       }
 -
   #ifdef TCL_TEST
       if (Tcltest_Init(interp) == TCL_ERROR) {
       return TCL_ERROR;
 --- 106,117 ----
   Tcl_AppInit(interp)
       Tcl_Interp *interp;        /* Interpreter for application. */
   {
 +     if (Tcl_InitStubs(interp, TCL_VERSION, 1) == NULL) {
 +        return TCL_ERROR;
 +     }
       if (Tcl_Init(interp) == TCL_ERROR) {
       return TCL_ERROR;
       }
   #ifdef TCL_TEST
       if (Tcltest_Init(interp) == TCL_ERROR) {
       return TCL_ERROR;
 ***************
 *** 196,202 ****
 --- 215,225 ----
           }
       }
       }
 + #ifdef USE_TCL_STUBS
 +     argSpace = (char *) malloc(
 + #else
       argSpace = (char *) Tcl_Alloc(
 + #endif
           (unsigned) (size * sizeof(char *) + strlen(cmdLine) + 1));
       argv = (char **) argSpace;
       argSpace += size * sizeof(char *);

 Index: makefile.vc
 ===================================================================
 RCS file: /cvsroot/tcl/win/makefile.vc,v
 retrieving revision 1.42
 diff -c -r1.42 makefile.vc
 *** makefile.vc    1999/07/22 23:45:53    1.42
 --- makefile.vc    1999/08/09 04:13:55
 ***************
 *** 183,189 ****
       $(TMPDIR)\tclScan.obj \
       $(TMPDIR)\tclStringObj.obj \
       $(TMPDIR)\tclStubInit.obj \
 !     $(TMPDIR)\tclStubLib.obj \
       $(TMPDIR)\tclThread.obj \
       $(TMPDIR)\tclTimer.obj \
       $(TMPDIR)\tclUtf.obj \
 --- 183,189 ----
       $(TMPDIR)\tclScan.obj \
       $(TMPDIR)\tclStringObj.obj \
       $(TMPDIR)\tclStubInit.obj \
 ! #    $(TMPDIR)\tclStubLib.obj \
       $(TMPDIR)\tclThread.obj \
       $(TMPDIR)\tclTimer.obj \
       $(TMPDIR)\tclUtf.obj \
 ***************
 *** 206,211 ****
 --- 206,213 ----
       $(TMPDIR)\tclWinTime.obj

   TCLSTUBOBJS = $(TMPDIR)\tclStubLib.obj \
 +           $(TMPDIR)\tclStubLoader.obj \
 +           $(TMPDIR)\tclWinDlLoad.obj

   cc32        = "$(TOOLS32)\bin\cl.exe"
   link32        = "$(TOOLS32)\bin\link.exe"
 ***************
 *** 356,365 ****
   $(TCLOBJS)
   <<

 ! $(TCLSH): $(TCLSHOBJS) $(TCLLIB) $(TMPDIR)\tclsh.res
       set LIB="$(TOOLS32)\lib"
       $(link32) $(ldebug) $(conlflags) $(TMPDIR)\tclsh.res -stack:2300000 \
 !         -out:$@ $(conlibsdll) $(TCLLIB) $(TCLSHOBJS)

   $(TCLSHP): $(TCLSHOBJS) $(TCLPLUGINLIB) $(TMPDIR)\tclsh.res
       set LIB=$(TOOLS32)\lib
 --- 358,367 ----
   $(TCLOBJS)
   <<

 ! $(TCLSH): $(TCLSHOBJS) $(TCLSTUBLIB) $(TMPDIR)\tclsh.res
       set LIB="$(TOOLS32)\lib"
       $(link32) $(ldebug) $(conlflags) $(TMPDIR)\tclsh.res -stack:2300000 \
 !         -out:$@ $(conlibsdll) $(TCLSTUBLIB) $(TCLSHOBJS)

   $(TCLSHP): $(TCLSHOBJS) $(TCLPLUGINLIB) $(TMPDIR)\tclsh.res
       set LIB=$(TOOLS32)\lib
 ***************
 *** 514,523 ****
   $(TMPDIR)\tclWinTest.obj: $(WINDIR)\tclWinTest.c
       $(cc32) $(TCL_CFLAGS) -Fo$@ $?

 - $(TMPDIR)\tclAppInit.obj : $(WINDIR)\tclAppInit.c
 -     $(cc32) $(TCL_CFLAGS) -Fo$@ $?
 -
   # The following objects should be built using the stub interfaces

   $(TMPDIR)\tclWinReg.obj : $(WINDIR)\tclWinReg.c
       $(cc32) $(TCL_CFLAGS) -DUSE_TCL_STUBS -Fo$@ $?
 --- 516,530 ----
   $(TMPDIR)\tclWinTest.obj: $(WINDIR)\tclWinTest.c
       $(cc32) $(TCL_CFLAGS) -Fo$@ $?

   # The following objects should be built using the stub interfaces
 +
 + !IF "$(NODEBUG)" == "1"
 + $(TMPDIR)\tclAppInit.obj : $(WINDIR)\tclAppInit.c
 +     $(cc32) $(TCL_CFLAGS) -DUSE_TCL_STUBS -Fo$@ $?
 + !ELSE
 + $(TMPDIR)\tclAppInit.obj : $(WINDIR)\tclAppInit.c
 +     $(cc32) $(TCL_CFLAGS) -DUSE_TCL_STUBS -DDEBUG -Fo$@ $?
 + !ENDIF

   $(TMPDIR)\tclWinReg.obj : $(WINDIR)\tclWinReg.c
       $(cc32) $(TCL_CFLAGS) -DUSE_TCL_STUBS -Fo$@ $?

 Index: e:/tclcvs/tcl8.2b1/win/tclWinDlLoad.c
 *** /dev/nul  Mon Aug 09 04:31:33 1999
 --- e:/tclcvs/tcl8.2b1/win/tclWinDlLoad.c       Mon Aug 09 02:58:32 1999
 ***************
 *** 0 ****
 --- 1,146 ----
 + /*
 +  * tclWinDlLoad.c --
 +  *
 +  * This procedure provides a version of dlopen() that
 +  * works with the Windows "LoadLibrary" and "GetProcAddress"
 +  * API for dynamic loading.
 +  *
 +  * These routines do not contain any dependancies for the
 +  * Tcl library so they can be used specificly for loading one
 +  * in a Stub'd application.
 +  *
 +  * They could also be integrated into TclpLoadFile, should it be desir
 +  *
 +  * code from a c.l.t post:
 +  * Sub: [Request Tcl 8.1] Dynamical loading enhancement for extensions
 +  * Date: 05/20/1999
 +  * Author: Jan Nijtmans <jan.nijtmans@wxs.nl>
 +  *
 +  * RCS: @(#) $Id:$
 +  *
 +  */
 +
 + #define WIN32_LEAN_AND_MEAN
 + #include <windows.h>
 + #undef WIN32_LEAN_AND_MEAN
 +
 + #define _TCLDECLS  /* lets make sure we don't touch ANY Tcl library fu
 */
 + #include "../compat/dlfcn.h"
 +
 + #define WINERRMSGBUFSIZE 1024
 +
 + char WinErrorMsg[WINERRMSGBUFSIZE];
 +
 + /*
 +  *--------------------------------------------------------------------
 +  *
 +  * dlopen --
 +  *
 +  * This function is an alternative for the functions
 +  * TclWinLoadLibrary and TclWinGetTclInstance.
 +  *
 +  * Results:
 +  *  Returns the handle of the newly loaded library, or NULL on
 +  *  failure. If path is NULL, the global library instance handle
 +  *  is returned.
 +  *
 +  * Side effects:
 +  *  Loads the specified library into the process.
 +  *
 +  *--------------------------------------------------------------------
 +  */
 +
 + VOID *dlopen(path, mode)
 +    const char *path;
 +    int mode;
 + {
 +    HMODULE mod;
 +    mod = LoadLibrary(path);
 +    if (mod <= (HMODULE) HINSTANCE_ERROR) return NULL;
 +    return (VOID *) mod;
 + };
 +
 +
 + /*
 +  *--------------------------------------------------------------------
 +  *
 +  * dlclose --
 +  *
 +  * This function is an alternative for the function
 +  * FreeLibrary.  It is responsible for removing library
 +  * handles from the library list and remove the dll
 +  * from memory.
 +  *
 +  * Results:
 +  *  -1 on error, 0 on success.
 +  *
 +  * Side effects:
 +  *  Unmaps and removes the specified library from the process.
 +  *
 +  *--------------------------------------------------------------------
 +  */
 +
 + int
 + dlclose(handle)
 +    VOID *handle;
 + {
 +   BOOL rtn;
 +
 +   rtn = FreeLibrary((HMODULE) handle);
 +   if (rtn == TRUE) return 0;
 +   return -1;
 + }
 +
 + /*
 +  *--------------------------------------------------------------------
 +  *
 +  * dlsym --
 +  *
 +  * This function is an alternative for the system function
 +  * GetProcAddress. It returns the address of a
 +  * symbol, give the handle returned by dlopen().
 +  *
 +  * Results:
 +  *  Returns the address of the symbol in the dll.
 +  *
 +  * Side effects:
 +  *  None.
 +  *
 +  *--------------------------------------------------------------------
 +  */
 +
 + VOID *dlsym(handle, symbol)
 +    VOID *handle;
 +    CONST char *symbol;
 + {
 +    return (VOID *) GetProcAddress((HMODULE) handle, symbol);
 + }
 +
 + /*
 +  *--------------------------------------------------------------------
 +  *
 +  * dlerror --
 +  *
 +  * This function returns a string describing the error which
 +  * occurred in dlopen().
 +  *
 +  * Results:
 +  *  Returns an error message.
 +  *
 +  * Side effects:
 +  *  errno is not set.
 +  *
 +  *--------------------------------------------------------------------
 +  */
 +
 + char *
 + dlerror()
 + {
 +   FormatMessage(
 +       FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
 +       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 +       WinErrorMsg, WINERRMSGBUFSIZE, NULL
 +       );
 +
 +   return WinErrorMsg;
 + }

 Index: e:/tclcvs/tcl8.2b1/generic/tclStubLoader.c
 *** /dev/nul    Mon Aug 09 04:31:33 1999
 --- e:/tclcvs/tcl8.2b1/generic/tclStubLoader.c    Mon Aug 09 19:09:36 1999
 ***************
 *** 0 ****
 --- 1,479 ----
 + /*
 +  * tclStubLoader.c
 +  *
 +  *    This file contains the routines for finding, dynamicaly loading
 +  *    and initializing the Stubs table in the Tcl library for a shell
 +  *    or any other Tcl application.
 +  *
 +  *
 +  * Copyright (c) 1999 by David Gravereaux.
 +  *
 +  * See the file "license.terms" for information on usage and redistribution
 +  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 +  *
 +  * RCS: @(#) $Id:$
 +  */
 +
 + #define USE_TCL_STUBS
 + #include "tclPort.h"
 + #include "../compat/dlfcn.h"
 +
 +
 + #ifdef __WIN32__
 + char *prefix = "\\bin\\tcl";
 + #else
 + char *prefix = "/bin/libtcl";  /* fix me! */
 + #endif
 +
 + char *shLibExt = TCL_SHLIB_EXT;
 + char *tclvers[] = { "8.4", "8.3", "8.2", "8.1", NULL };
 + VOID *stubTclLibHnd = NULL;
 + char *stubLoadErrorMsg;
 +
 + typedef Tcl_Interp *(*LPFN_createInterpProc) ();
 +
 + /*
 +  * Prototypes for functions used only in this file.
 +  */
 +
 + static VOID stubBuildTrailing _ANSI_ARGS_((char *pos, char *ver, int dbgOnly));
 +
 +
 + 
 + /*
 +  *-------------------------------------------------------------------------
 +  *
 +  * Stub_PreInit --
 +  *
 +  *    Initializes the Stubs table and the lib paths.  Use this for when
 +  *  you don't use Tcl_Main.
 +  *
 +  * Results:
 +  *    none
 +  *
 +  * Side effects:
 +  *    wastes an interp, but the Stubs table is all filled.
 +  *
 +  *-------------------------------------------------------------------------
 +  */
 +
 + VOID
 + Stub_PreInit(ver, exact, app)
 +    char *ver;
 +    int exact;
 +    char *app;
 + {
 +  Tcl_Interp *interp;
 +  LPFN_createInterpProc createInterpProc;
 +
 +   createInterpProc =
 +       (LPFN_createInterpProc) dlsym(stubTclLibHnd, "Tcl_CreateInterp");
 +
 +   interp = createInterpProc();
 +   Tcl_InitStubs(interp, ver, exact);
 +   Tcl_FindExecutable(app);
 +   Tcl_DeleteInterp(interp);
 + }
 + 
 + /*
 +  *-------------------------------------------------------------------------
 +  *
 +  * Stub_GetMain --
 +  *
 +  *    Just grabs the location of Tcl_Main.
 +  *
 +  * Results:
 +  *    pointer to the Tcl_Main function.
 +  *
 +  * Side effects:
 +  *    bypasses some type safety and just returns a VOID *.
 +  *
 +  *-------------------------------------------------------------------------
 +  */
 +
 + VOID *
 + Stub_GetMain ()
 + {
 +   return dlsym(stubTclLibHnd, "Tcl_Main");
 + }
 + 
 + /*
 +  *-------------------------------------------------------------------------
 +  *
 +  * Stub_GetCreateInterp --
 +  *
 +  *    Just grabs the location of Tcl_CreateInterp.  Use this when you don't
 +  *  want to waste an interp with Stub_PreInit.
 +  *
 +  * Results:
 +  *    pointer to the Tcl_CreateInterp function.
 +  *
 +  * Side effects:
 +  *    bypasses some type safety and just returns a VOID *.
 +  *
 +  *-------------------------------------------------------------------------
 +  */
 +
 + VOID *
 + Stub_GetCreateInterp ()
 + {
 +   return dlsym(stubTclLibHnd, "Tcl_CreateInterp");
 + }
 + 
 + /*
 +  *-------------------------------------------------------------------------
 +  *
 +  * Stub_LoadTcl --
 +  *
 +  *    Searches the system and loads the proper Tcl library into memory.
 +  *
 +  *  Checks:   1) TCL_LIBRARY_PATH first
 +  *            2) Registry (windows only) search for
 +  *               HKEY_LOCAL_MACHINE\SOFTWARE\Scriptics\Tcl
 +  *            3) current directory (or default path) search
 +  *
 +  *  When exact is set to 0, a range of versions is tried, starting
 +  *  at the top of the tclvers array and working down.
 +  *
 +  * Results:
 +  *    The version actually loaded or NULL on error
 +  *
 +  * Side effects:
 +  *    Tcl is now in memory.
 +  *  BUG: running tclsh from the development environment will load
 +  *       the installed one rather than the Tcl library sitting next
 +  *       to it.
 +  *
 +  *-------------------------------------------------------------------------
 +  */
 +
 + char *
 + Stub_LoadTcl (minVer, exact, dbgOnly)
 +   char *minVer;    /* minimum version acceptable */
 +   int exact;       /* only the specified version will do */
 +   int dbgOnly;     /* load the debug (symbol) library */
 + {
 +  char *tclRootPath = NULL;
 +  char stubTclLibFullPath[_MAX_PATH];
 +  char *savedPos;
 +  char *pos1, *pos2;
 + #ifdef __WIN32__
 +  HKEY topTclKey;
 +  HKEY subTclVer;
 +  DWORD max_path = _MAX_PATH;
 +  LONG result;
 + #endif
 +
 +   /*
 +    * Try the environment variable first.  This overrides everything.
 +    */
 +   tclRootPath = getenv("TCL_LIBRARY_PATH");
 +   if (tclRootPath != NULL) {
 +
 +     pos1 = stubTclLibFullPath;
 +     while (*tclRootPath != '\0') {
 +       *pos1++ = *tclRootPath++;
 +     };                              /* ex. c:\program files\tcl */
 +
 +     /*
 +      * Append the bin directory and prefix
 +      */
 +     pos2 = prefix;
 +     while (*pos2 != '\0') {
 +       *pos1++ = *pos2++;
 +     };                              /* ex. c:\program files\tcl\bin\tcl */
 +     savedPos = pos1;
 +
 +     if (exact != 0) {
 +
 +       /*
 +        * build the end of the filename from the parameter given
 +        */
 +       stubBuildTrailing(savedPos, minVer, dbgOnly);
 +                                     /* ex. c:\program files\tcl\bin\tcl81d.dll */
 +       /* Try it */
 +       if ((stubTclLibHnd = dlopen(stubTclLibFullPath, RTLD_NOW)) != NULL)
 +           return minVer;
 +
 +     } else {
 +      int i = 0;
 +
 +       /*
 +        * Try a range of filenames starting at the top and working down
 +        */
 +       for (i = 0; tclvers[i] != NULL && strcmp(tclvers[i],minVer) >= 0; i++) {
 +
 +         /*
 +          * build the end of the filename
 +          */
 +         stubBuildTrailing(savedPos, tclvers[i], dbgOnly);
 +                                     /* ex. c:\program files\tcl\bin\tcl84d.dll */
 +         /* Try it */
 +         if ((stubTclLibHnd = dlopen(stubTclLibFullPath, RTLD_NOW)) != NULL)
 +             return tclvers[i];
 +       };
 +     }
 +
 +     /*
 +      * A suitable Tcl library hasn't been found yet.
 +      * Keep falling through.
 +      */
 +   }
 +
 + #ifdef __WIN32__
 +
 +   /*
 +    * Do a registry search
 +    */
 +   result = RegOpenKeyEx(
 +       HKEY_LOCAL_MACHINE, "SOFTWARE\\Scriptics\\Tcl", 0,
 +       KEY_ENUMERATE_SUB_KEYS, &topTclKey
 +       );
 +
 +   if (result != ERROR_SUCCESS) {
 +     goto Next;
 +   };
 +
 +   if (exact != 0) {
 +
 +     /*
 +      * Just try the requested only
 +      */
 +     result = RegOpenKeyEx(topTclKey, minVer, 0, KEY_READ, &subTclVer);
 +
 +     if (result != ERROR_SUCCESS) {
 +       /* Subkey for exact version not found. */
 +       goto Next;
 +     };
 +
 +     /*
 +      * Retrieve location and load it.
 +      */
 +     result = RegQueryValueEx(
 +         subTclVer, "Root", NULL, NULL, stubTclLibFullPath, &max_path
 +         );
 +
 +     if (result != ERROR_SUCCESS) {
 +       stubLoadErrorMsg = "SubKey found in registry for exact match, but no Root value set.";
 +       return NULL;
 +     };
 +
 +     pos1 = stubTclLibFullPath;
 +     while (*pos1 != '\0') { pos1++; }
 +
 +     /*
 +      * Append the bin directory and prefix
 +      */
 +     pos2 = prefix;
 +     while (*pos2 != '\0') {
 +       *pos1++ = *pos2++;
 +     };
 +     savedPos = pos1;
 +
 +     /*
 +      * build the end of the filename from the parameter given
 +      */
 +     stubBuildTrailing(savedPos, minVer, dbgOnly);
 +
 +     /* Try it */
 +     if ((stubTclLibHnd = dlopen(stubTclLibFullPath, RTLD_NOW)) != NULL)
 +         return minVer;
 +
 +   } else {
 +    int i;
 +
 +     /*
 +      * Try a range of filenames starting at the top and working down
 +      */
 +     for (i = 0; tclvers[i] != NULL && strcmp(tclvers[i],minVer) >= 0; i++) {
 +
 +       result = RegOpenKeyEx(topTclKey, tclvers[i], 0, KEY_READ, &subTclVer);
 +
 +       if (result != ERROR_SUCCESS) {
 +         continue;
 +       };
 +
 +       /* Try it. */
 +       result = RegQueryValueEx(
 +           subTclVer, "Root", NULL, NULL, stubTclLibFullPath, &max_path
 +           );
 +
 +       if (result != ERROR_SUCCESS) {
 +         /* Odd...  SubKey is there, but no Root value. */
 +         continue;
 +       };
 +
 +       pos1 = stubTclLibFullPath;
 +       while (*pos1 != '\0') { pos1++; }
 +
 +       /*
 +        * Append the bin directory and prefix
 +        */
 +       pos2 = prefix;
 +       while (*pos2 != '\0') {
 +         *pos1++ = *pos2++;
 +       };
 +       savedPos = pos1;
 +
 +       /*
 +        * Build the end of the filename
 +        */
 +       stubBuildTrailing(savedPos, tclvers[i], dbgOnly);
 +
 +       /* Try it */
 +       if ((stubTclLibHnd = dlopen(stubTclLibFullPath, RTLD_NOW)) != NULL)
 +           return tclvers[i];
 +     };
 +
 +   /*
 +    * A suitable Tcl library hasn't been found yet.
 +    * Keep falling through.
 +    */
 +
 +   };
 + Next:
 + #endif  /* __WIN32__ */
 +
 +   /*
 +    * Try current directory or default search path.
 +    * Last chance.  You better work.
 +    */
 +   pos1 = stubTclLibFullPath;
 +
 + #ifdef __WIN32__
 +   pos2 = "tcl";
 + #else
 +   pos2 = "libtcl";  /* fix me! */
 + #endif
 +
 +   while (*pos2 != '\0') {
 +     *pos1++ = *pos2++;
 +   };
 +   savedPos = pos1;
 +
 +
 +   if (exact != 0) {
 +     stubBuildTrailing(savedPos, minVer, dbgOnly);
 +
 +     /* Try it */
 +     if ((stubTclLibHnd = dlopen(stubTclLibFullPath, RTLD_NOW)) != NULL)
 +         return minVer;
 +
 +   } else {
 +    int i;
 +
 +     /*
 +      * Try a range of filenames starting at the top and working down
 +      */
 +     for (i = 0; tclvers[i] != NULL && strcmp(tclvers[i],minVer) >= 0; i++) {
 +       /*
 +        * Build the end of the filename
 +        */
 +       stubBuildTrailing(savedPos, tclvers[i], dbgOnly);
 +
 +       /* Try it */
 +       if ((stubTclLibHnd = dlopen(stubTclLibFullPath, RTLD_NOW)) != NULL)
 +           return tclvers[i];
 +     };
 +   }
 +
 +   stubLoadErrorMsg = "A proper Tcl library could not be located anywhere.";
 +   return NULL;
 + }
 + 
 + /*
 +  *-------------------------------------------------------------------------
 +  *
 +  * Stub_UnLoadTcl --
 +  *
 +  *    Unloads the Tcl library into memory.
 +  *
 +  * Results:
 +  *    none.
 +  *
 +  * Side effects:
 +  *    The Stubs table is now invalid.
 +  *
 +  *-------------------------------------------------------------------------
 +  */
 +
 + VOID
 + Stub_UnLoadTcl ()
 + {
 +   if (stubTclLibHnd != NULL) dlclose(stubTclLibHnd);
 + };
 + 
 + /*
 +  *-------------------------------------------------------------------------
 +  *
 +  * Stub_LoadTclError --
 +  *
 +  *    Gets the associated error message for when Stub_LoadTcl returns NULL.
 +  *
 +  * Results:
 +  *    the message.
 +  *
 +  * Side effects:
 +  *    none.
 +  *
 +  *-------------------------------------------------------------------------
 +  */
 +
 + char *
 + Stub_LoadTclError()
 + {
 +   return stubLoadErrorMsg;
 + };
 + 
 + /*
 +  *-------------------------------------------------------------------------
 +  *
 +  * stubBuildTrailing --
 +  *
 +  *    Helper function for Stub_LoadTcl that finishes and terminates
 +  *  the path string.
 +  *
 +  * Results:
 +  *    none.
 +  *
 +  * Side effects:
 +  *    writes on the given memory location.
 +  *
 +  *-------------------------------------------------------------------------
 +  */
 +
 + static VOID
 + stubBuildTrailing(pos, ver, dbgOnly)
 +   char *pos;
 +   char *ver;
 +   int dbgOnly;
 + {
 +  char *pos2;
 +
 + #ifdef __WIN32__
 +   /* append version w/o decimal */
 +   pos2 = ver;
 +   while (*pos2 != '\0') {
 +     if (*pos2 != '.') *pos++ = *pos2++;
 +     else pos2++;
 +   };
 + #else     /* fix me! */
 +   /* append version w/ decimal */
 +   pos2 = ver;
 +   while (*pos2 != '\0') {
 +     *pos++ = *pos2++;
 +   };
 + #endif
 +
 +   /* if asked, add the debug suffix */
 +   if (dbgOnly != 0) *pos++ = 'd';
 +
 +   /* append the extension */
 +   pos2 = shLibExt;
 +   while (*pos2 != '\0') {
 +     *pos++ = *pos2++;
 +   };
 +
 +   /* terminate it */
 +   *pos = '\0';
 + };

 PatchFiles:
     generic/tcl.h, win/tclAppInit.c, win/makefile.vc

 Comments:
     This thing needs alpha testers.  Give me some input :)

Last modified
1999-09-27

(195.108.246.50)

Note: you are looking at
the snapshot of an old wiki
- much of this information
is likely to be very outdated