tclLoad.c

Go to the documentation of this file.
00001 /*
00002  * tclLoad.c --
00003  *
00004  *      This file provides the generic portion (those that are the same on all
00005  *      platforms) of Tcl's dynamic loading facilities.
00006  *
00007  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
00008  *
00009  * See the file "license.terms" for information on usage and redistribution of
00010  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00011  *
00012  * RCS: @(#) $Id: tclLoad.c,v 1.16 2007/02/20 23:24:03 nijtmans Exp $
00013  */
00014 
00015 #include "tclInt.h"
00016 
00017 /*
00018  * The following structure describes a package that has been loaded either
00019  * dynamically (with the "load" command) or statically (as indicated by a call
00020  * to TclGetLoadedPackages). All such packages are linked together into a
00021  * single list for the process. Packages are never unloaded, until the
00022  * application exits, when TclFinalizeLoad is called, and these structures are
00023  * freed.
00024  */
00025 
00026 typedef struct LoadedPackage {
00027     char *fileName;             /* Name of the file from which the package was
00028                                  * loaded. An empty string means the package
00029                                  * is loaded statically. Malloc-ed. */
00030     char *packageName;          /* Name of package prefix for the package,
00031                                  * properly capitalized (first letter UC,
00032                                  * others LC), no "_", as in "Net".
00033                                  * Malloc-ed. */
00034     Tcl_LoadHandle loadHandle;  /* Token for the loaded file which should be
00035                                  * passed to (*unLoadProcPtr)() when the file
00036                                  * is no longer needed. If fileName is NULL,
00037                                  * then this field is irrelevant. */
00038     Tcl_PackageInitProc *initProc;
00039                                 /* Initialization function to call to
00040                                  * incorporate this package into a trusted
00041                                  * interpreter. */
00042     Tcl_PackageInitProc *safeInitProc;
00043                                 /* Initialization function to call to
00044                                  * incorporate this package into a safe
00045                                  * interpreter (one that will execute
00046                                  * untrusted scripts). NULL means the package
00047                                  * can't be used in unsafe interpreters. */
00048     Tcl_PackageUnloadProc *unloadProc;
00049                                 /* Finalisation function to unload a package
00050                                  * from a trusted interpreter. NULL means that
00051                                  * the package cannot be unloaded. */
00052     Tcl_PackageUnloadProc *safeUnloadProc;
00053                                 /* Finalisation function to unload a package
00054                                  * from a safe interpreter. NULL means that
00055                                  * the package cannot be unloaded. */
00056     int interpRefCount;         /* How many times the package has been loaded
00057                                  * in trusted interpreters. */
00058     int safeInterpRefCount;     /* How many times the package has been loaded
00059                                  * in safe interpreters. */
00060     Tcl_FSUnloadFileProc *unLoadProcPtr;
00061                                 /* Function to use to unload this package. If
00062                                  * NULL, then we do not attempt to unload the
00063                                  * package. If fileName is NULL, then this
00064                                  * field is irrelevant. */
00065     struct LoadedPackage *nextPtr;
00066                                 /* Next in list of all packages loaded into
00067                                  * this application process. NULL means end of
00068                                  * list. */
00069 } LoadedPackage;
00070 
00071 /*
00072  * TCL_THREADS
00073  * There is a global list of packages that is anchored at firstPackagePtr.
00074  * Access to this list is governed by a mutex.
00075  */
00076 
00077 static LoadedPackage *firstPackagePtr = NULL;
00078                                 /* First in list of all packages loaded into
00079                                  * this process. */
00080 
00081 TCL_DECLARE_MUTEX(packageMutex)
00082 
00083 /*
00084  * The following structure represents a particular package that has been
00085  * incorporated into a particular interpreter (by calling its initialization
00086  * function). There is a list of these structures for each interpreter, with
00087  * an AssocData value (key "load") for the interpreter that points to the
00088  * first package (if any).
00089  */
00090 
00091 typedef struct InterpPackage {
00092     LoadedPackage *pkgPtr;      /* Points to detailed information about
00093                                  * package. */
00094     struct InterpPackage *nextPtr;
00095                                 /* Next package in this interpreter, or NULL
00096                                  * for end of list. */
00097 } InterpPackage;
00098 
00099 /*
00100  * Prototypes for functions that are private to this file:
00101  */
00102 
00103 static void             LoadCleanupProc(ClientData clientData,
00104                             Tcl_Interp *interp);
00105 
00106 /*
00107  *----------------------------------------------------------------------
00108  *
00109  * Tcl_LoadObjCmd --
00110  *
00111  *      This function is invoked to process the "load" Tcl command. See the
00112  *      user documentation for details on what it does.
00113  *
00114  * Results:
00115  *      A standard Tcl result.
00116  *
00117  * Side effects:
00118  *      See the user documentation.
00119  *
00120  *----------------------------------------------------------------------
00121  */
00122 
00123 int
00124 Tcl_LoadObjCmd(
00125     ClientData dummy,           /* Not used. */
00126     Tcl_Interp *interp,         /* Current interpreter. */
00127     int objc,                   /* Number of arguments. */
00128     Tcl_Obj *const objv[])      /* Argument objects. */
00129 {
00130     Tcl_Interp *target;
00131     LoadedPackage *pkgPtr, *defaultPtr;
00132     Tcl_DString pkgName, tmp, initName, safeInitName;
00133     Tcl_DString unloadName, safeUnloadName;
00134     Tcl_PackageInitProc *initProc, *safeInitProc, *unloadProc, *safeUnloadProc;
00135     InterpPackage *ipFirstPtr, *ipPtr;
00136     int code, namesMatch, filesMatch, offset;
00137     const char *symbols[4];
00138     Tcl_PackageInitProc **procPtrs[4];
00139     ClientData clientData;
00140     char *p, *fullFileName, *packageName;
00141     Tcl_LoadHandle loadHandle;
00142     Tcl_FSUnloadFileProc *unLoadProcPtr = NULL;
00143     Tcl_UniChar ch;
00144 
00145     if ((objc < 2) || (objc > 4)) {
00146         Tcl_WrongNumArgs(interp, 1, objv, "fileName ?packageName? ?interp?");
00147         return TCL_ERROR;
00148     }
00149     if (Tcl_FSConvertToPathType(interp, objv[1]) != TCL_OK) {
00150         return TCL_ERROR;
00151     }
00152     fullFileName = Tcl_GetString(objv[1]);
00153 
00154     Tcl_DStringInit(&pkgName);
00155     Tcl_DStringInit(&initName);
00156     Tcl_DStringInit(&safeInitName);
00157     Tcl_DStringInit(&unloadName);
00158     Tcl_DStringInit(&safeUnloadName);
00159     Tcl_DStringInit(&tmp);
00160 
00161     packageName = NULL;
00162     if (objc >= 3) {
00163         packageName = Tcl_GetString(objv[2]);
00164         if (packageName[0] == '\0') {
00165             packageName = NULL;
00166         }
00167     }
00168     if ((fullFileName[0] == 0) && (packageName == NULL)) {
00169         Tcl_SetResult(interp,
00170                 "must specify either file name or package name",
00171                 TCL_STATIC);
00172         code = TCL_ERROR;
00173         goto done;
00174     }
00175 
00176     /*
00177      * Figure out which interpreter we're going to load the package into.
00178      */
00179 
00180     target = interp;
00181     if (objc == 4) {
00182         char *slaveIntName = Tcl_GetString(objv[3]);
00183 
00184         target = Tcl_GetSlave(interp, slaveIntName);
00185         if (target == NULL) {
00186             code = TCL_ERROR;
00187             goto done;
00188         }
00189     }
00190 
00191     /*
00192      * Scan through the packages that are currently loaded to see if the
00193      * package we want is already loaded. We'll use a loaded package if it
00194      * meets any of the following conditions:
00195      *  - Its name and file match the once we're looking for.
00196      *  - Its file matches, and we weren't given a name.
00197      *  - Its name matches, the file name was specified as empty, and there is
00198      *    only no statically loaded package with the same name.
00199      */
00200 
00201     Tcl_MutexLock(&packageMutex);
00202 
00203     defaultPtr = NULL;
00204     for (pkgPtr = firstPackagePtr; pkgPtr != NULL; pkgPtr = pkgPtr->nextPtr) {
00205         if (packageName == NULL) {
00206             namesMatch = 0;
00207         } else {
00208             Tcl_DStringSetLength(&pkgName, 0);
00209             Tcl_DStringAppend(&pkgName, packageName, -1);
00210             Tcl_DStringSetLength(&tmp, 0);
00211             Tcl_DStringAppend(&tmp, pkgPtr->packageName, -1);
00212             Tcl_UtfToLower(Tcl_DStringValue(&pkgName));
00213             Tcl_UtfToLower(Tcl_DStringValue(&tmp));
00214             if (strcmp(Tcl_DStringValue(&tmp),
00215                     Tcl_DStringValue(&pkgName)) == 0) {
00216                 namesMatch = 1;
00217             } else {
00218                 namesMatch = 0;
00219             }
00220         }
00221         Tcl_DStringSetLength(&pkgName, 0);
00222 
00223         filesMatch = (strcmp(pkgPtr->fileName, fullFileName) == 0);
00224         if (filesMatch && (namesMatch || (packageName == NULL))) {
00225             break;
00226         }
00227         if (namesMatch && (fullFileName[0] == 0)) {
00228             defaultPtr = pkgPtr;
00229         }
00230         if (filesMatch && !namesMatch && (fullFileName[0] != 0)) {
00231             /*
00232              * Can't have two different packages loaded from the same file.
00233              */
00234 
00235             Tcl_AppendResult(interp, "file \"", fullFileName,
00236                     "\" is already loaded for package \"",
00237                     pkgPtr->packageName, "\"", NULL);
00238             code = TCL_ERROR;
00239             Tcl_MutexUnlock(&packageMutex);
00240             goto done;
00241         }
00242     }
00243     Tcl_MutexUnlock(&packageMutex);
00244     if (pkgPtr == NULL) {
00245         pkgPtr = defaultPtr;
00246     }
00247 
00248     /*
00249      * Scan through the list of packages already loaded in the target
00250      * interpreter. If the package we want is already loaded there, then
00251      * there's nothing for us to do.
00252      */
00253 
00254     if (pkgPtr != NULL) {
00255         ipFirstPtr = (InterpPackage *) Tcl_GetAssocData(target,
00256                 "tclLoad", NULL);
00257         for (ipPtr = ipFirstPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
00258             if (ipPtr->pkgPtr == pkgPtr) {
00259                 code = TCL_OK;
00260                 goto done;
00261             }
00262         }
00263     }
00264 
00265     if (pkgPtr == NULL) {
00266         /*
00267          * The desired file isn't currently loaded, so load it. It's an error
00268          * if the desired package is a static one.
00269          */
00270 
00271         if (fullFileName[0] == 0) {
00272             Tcl_AppendResult(interp, "package \"", packageName,
00273                     "\" isn't loaded statically", NULL);
00274             code = TCL_ERROR;
00275             goto done;
00276         }
00277 
00278         /*
00279          * Figure out the module name if it wasn't provided explicitly.
00280          */
00281 
00282         if (packageName != NULL) {
00283             Tcl_DStringAppend(&pkgName, packageName, -1);
00284         } else {
00285             int retc;
00286 
00287             /*
00288              * Threading note - this call used to be protected by a mutex.
00289              */
00290 
00291             retc = TclGuessPackageName(fullFileName, &pkgName);
00292             if (!retc) {
00293                 Tcl_Obj *splitPtr;
00294                 Tcl_Obj *pkgGuessPtr;
00295                 int pElements;
00296                 char *pkgGuess;
00297 
00298                 /*
00299                  * The platform-specific code couldn't figure out the module
00300                  * name. Make a guess by taking the last element of the file
00301                  * name, stripping off any leading "lib", and then using all
00302                  * of the alphabetic and underline characters that follow
00303                  * that.
00304                  */
00305 
00306                 splitPtr = Tcl_FSSplitPath(objv[1], &pElements);
00307                 Tcl_ListObjIndex(NULL, splitPtr, pElements -1, &pkgGuessPtr);
00308                 pkgGuess = Tcl_GetString(pkgGuessPtr);
00309                 if ((pkgGuess[0] == 'l') && (pkgGuess[1] == 'i')
00310                         && (pkgGuess[2] == 'b')) {
00311                     pkgGuess += 3;
00312                 }
00313                 for (p = pkgGuess; *p != 0; p += offset) {
00314                     offset = Tcl_UtfToUniChar(p, &ch);
00315                     if ((ch > 0x100)
00316                             || !(isalpha(UCHAR(ch)) /* INTL: ISO only */
00317                                     || (UCHAR(ch) == '_'))) {
00318                         break;
00319                     }
00320                 }
00321                 if (p == pkgGuess) {
00322                     Tcl_DecrRefCount(splitPtr);
00323                     Tcl_AppendResult(interp,
00324                             "couldn't figure out package name for ",
00325                             fullFileName, NULL);
00326                     code = TCL_ERROR;
00327                     goto done;
00328                 }
00329                 Tcl_DStringAppend(&pkgName, pkgGuess, (p - pkgGuess));
00330                 Tcl_DecrRefCount(splitPtr);
00331             }
00332         }
00333 
00334         /*
00335          * Fix the capitalization in the package name so that the first
00336          * character is in caps (or title case) but the others are all
00337          * lower-case.
00338          */
00339 
00340         Tcl_DStringSetLength(&pkgName,
00341                 Tcl_UtfToTitle(Tcl_DStringValue(&pkgName)));
00342 
00343         /*
00344          * Compute the names of the two initialization functions, based on the
00345          * package name.
00346          */
00347 
00348         Tcl_DStringAppend(&initName, Tcl_DStringValue(&pkgName), -1);
00349         Tcl_DStringAppend(&initName, "_Init", 5);
00350         Tcl_DStringAppend(&safeInitName, Tcl_DStringValue(&pkgName), -1);
00351         Tcl_DStringAppend(&safeInitName, "_SafeInit", 9);
00352         Tcl_DStringAppend(&unloadName, Tcl_DStringValue(&pkgName), -1);
00353         Tcl_DStringAppend(&unloadName, "_Unload", 7);
00354         Tcl_DStringAppend(&safeUnloadName, Tcl_DStringValue(&pkgName), -1);
00355         Tcl_DStringAppend(&safeUnloadName, "_SafeUnload", 11);
00356 
00357         /*
00358          * Call platform-specific code to load the package and find the two
00359          * initialization functions.
00360          */
00361 
00362         symbols[0] = Tcl_DStringValue(&initName);
00363         symbols[1] = Tcl_DStringValue(&safeInitName);
00364         symbols[2] = Tcl_DStringValue(&unloadName);
00365         symbols[3] = Tcl_DStringValue(&safeUnloadName);
00366         procPtrs[0] = &initProc;
00367         procPtrs[1] = &safeInitProc;
00368         procPtrs[2] = &unloadProc;
00369         procPtrs[3] = &safeUnloadProc;
00370 
00371         Tcl_MutexLock(&packageMutex);
00372         code = TclLoadFile(interp, objv[1], 4, symbols, procPtrs,
00373                 &loadHandle, &clientData, &unLoadProcPtr);
00374         Tcl_MutexUnlock(&packageMutex);
00375         loadHandle = (Tcl_LoadHandle) clientData;
00376         if (code != TCL_OK) {
00377             goto done;
00378         }
00379 
00380         if (*procPtrs[0] /* initProc */ == NULL) {
00381             Tcl_AppendResult(interp, "couldn't find procedure ",
00382                     Tcl_DStringValue(&initName), NULL);
00383             if (unLoadProcPtr != NULL) {
00384                 (*unLoadProcPtr)(loadHandle);
00385             }
00386             code = TCL_ERROR;
00387             goto done;
00388         }
00389 
00390         /*
00391          * Create a new record to describe this package.
00392          */
00393 
00394         pkgPtr = (LoadedPackage *) ckalloc(sizeof(LoadedPackage));
00395         pkgPtr->fileName           = (char *) ckalloc((unsigned)
00396                 (strlen(fullFileName) + 1));
00397         strcpy(pkgPtr->fileName, fullFileName);
00398         pkgPtr->packageName        = (char *) ckalloc((unsigned)
00399                 (Tcl_DStringLength(&pkgName) + 1));
00400         strcpy(pkgPtr->packageName, Tcl_DStringValue(&pkgName));
00401         pkgPtr->loadHandle         = loadHandle;
00402         pkgPtr->unLoadProcPtr      = unLoadProcPtr;
00403         pkgPtr->initProc           = *procPtrs[0];
00404         pkgPtr->safeInitProc       = *procPtrs[1];
00405         pkgPtr->unloadProc         = (Tcl_PackageUnloadProc*) *procPtrs[2];
00406         pkgPtr->safeUnloadProc     = (Tcl_PackageUnloadProc*) *procPtrs[3];
00407         pkgPtr->interpRefCount     = 0;
00408         pkgPtr->safeInterpRefCount = 0;
00409 
00410         Tcl_MutexLock(&packageMutex);
00411         pkgPtr->nextPtr            = firstPackagePtr;
00412         firstPackagePtr            = pkgPtr;
00413         Tcl_MutexUnlock(&packageMutex);
00414     }
00415 
00416     /*
00417      * Invoke the package's initialization function (either the normal one or
00418      * the safe one, depending on whether or not the interpreter is safe).
00419      */
00420 
00421     if (Tcl_IsSafe(target)) {
00422         if (pkgPtr->safeInitProc != NULL) {
00423             code = (*pkgPtr->safeInitProc)(target);
00424         } else {
00425             Tcl_AppendResult(interp,
00426                     "can't use package in a safe interpreter: no ",
00427                     pkgPtr->packageName, "_SafeInit procedure", NULL);
00428             code = TCL_ERROR;
00429             goto done;
00430         }
00431     } else {
00432         code = (*pkgPtr->initProc)(target);
00433     }
00434 
00435     /*
00436      * Record the fact that the package has been loaded in the target
00437      * interpreter.
00438      */
00439 
00440     if (code == TCL_OK) {
00441         /*
00442          * Update the proper reference count.
00443          */
00444 
00445         Tcl_MutexLock(&packageMutex);
00446         if (Tcl_IsSafe(target)) {
00447             ++pkgPtr->safeInterpRefCount;
00448         } else {
00449             ++pkgPtr->interpRefCount;
00450         }
00451         Tcl_MutexUnlock(&packageMutex);
00452 
00453         /*
00454          * Refetch ipFirstPtr: loading the package may have introduced
00455          * additional static packages at the head of the linked list!
00456          */
00457 
00458         ipFirstPtr = (InterpPackage *) Tcl_GetAssocData(target,
00459                 "tclLoad", NULL);
00460         ipPtr = (InterpPackage *) ckalloc(sizeof(InterpPackage));
00461         ipPtr->pkgPtr = pkgPtr;
00462         ipPtr->nextPtr = ipFirstPtr;
00463         Tcl_SetAssocData(target, "tclLoad", LoadCleanupProc,
00464                 (ClientData) ipPtr);
00465     } else {
00466         TclTransferResult(target, code, interp);
00467     }
00468 
00469   done:
00470     Tcl_DStringFree(&pkgName);
00471     Tcl_DStringFree(&initName);
00472     Tcl_DStringFree(&safeInitName);
00473     Tcl_DStringFree(&unloadName);
00474     Tcl_DStringFree(&safeUnloadName);
00475     Tcl_DStringFree(&tmp);
00476     return code;
00477 }
00478 
00479 /*
00480  *----------------------------------------------------------------------
00481  *
00482  * Tcl_UnloadObjCmd --
00483  *
00484  *      This function is invoked to process the "unload" Tcl command. See the
00485  *      user documentation for details on what it does.
00486  *
00487  * Results:
00488  *      A standard Tcl result.
00489  *
00490  * Side effects:
00491  *      See the user documentation.
00492  *
00493  *----------------------------------------------------------------------
00494  */
00495 
00496 int
00497 Tcl_UnloadObjCmd(
00498     ClientData dummy,           /* Not used. */
00499     Tcl_Interp *interp,         /* Current interpreter. */
00500     int objc,                   /* Number of arguments. */
00501     Tcl_Obj *const objv[])      /* Argument objects. */
00502 {
00503     Tcl_Interp *target;         /* Which interpreter to unload from. */
00504     LoadedPackage *pkgPtr, *defaultPtr;
00505     Tcl_DString pkgName, tmp;
00506     Tcl_PackageUnloadProc *unloadProc;
00507     InterpPackage *ipFirstPtr, *ipPtr;
00508     int i, index, code, complain = 1, keepLibrary = 0;
00509     int trustedRefCount = -1, safeRefCount = -1;
00510     const char *fullFileName = "";
00511     char *packageName;
00512     static const char *options[] = {
00513         "-nocomplain", "-keeplibrary", "--", NULL
00514     };
00515     enum options {
00516         UNLOAD_NOCOMPLAIN, UNLOAD_KEEPLIB, UNLOAD_LAST
00517     };
00518 
00519     for (i = 1; i < objc; i++) {
00520         if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0,
00521                 &index) != TCL_OK) {
00522             fullFileName = Tcl_GetString(objv[i]);
00523             if (fullFileName[0] == '-') {
00524                 /*
00525                  * It looks like the command contains an option so signal an
00526                  * error
00527                  */
00528 
00529                 return TCL_ERROR;
00530             } else {
00531                 /*
00532                  * This clearly isn't an option; assume it's the filename. We
00533                  * must clear the error.
00534                  */
00535 
00536                 Tcl_ResetResult(interp);
00537                 break;
00538             }
00539         }
00540         switch (index) {
00541         case UNLOAD_NOCOMPLAIN:         /* -nocomplain */
00542             complain = 0;
00543             break;
00544         case UNLOAD_KEEPLIB:            /* -keeplibrary */
00545             keepLibrary = 1;
00546             break;
00547         case UNLOAD_LAST:               /* -- */
00548             i++;
00549             goto endOfForLoop;
00550         }
00551     }
00552   endOfForLoop:
00553     if ((objc-i < 1) || (objc-i > 3)) {
00554         Tcl_WrongNumArgs(interp, 1, objv,
00555                 "?switches? fileName ?packageName? ?interp?");
00556         return TCL_ERROR;
00557     }
00558     if (Tcl_FSConvertToPathType(interp, objv[i]) != TCL_OK) {
00559         return TCL_ERROR;
00560     }
00561 
00562     fullFileName = Tcl_GetString(objv[i]);
00563     Tcl_DStringInit(&pkgName);
00564     Tcl_DStringInit(&tmp);
00565 
00566     packageName = NULL;
00567     if (objc - i >= 2) {
00568         packageName = Tcl_GetString(objv[i+1]);
00569         if (packageName[0] == '\0') {
00570             packageName = NULL;
00571         }
00572     }
00573     if ((fullFileName[0] == 0) && (packageName == NULL)) {
00574         Tcl_SetResult(interp,
00575                 "must specify either file name or package name",
00576                 TCL_STATIC);
00577         code = TCL_ERROR;
00578         goto done;
00579     }
00580 
00581     /*
00582      * Figure out which interpreter we're going to load the package into.
00583      */
00584 
00585     target = interp;
00586     if (objc - i == 3) {
00587         char *slaveIntName;
00588         slaveIntName = Tcl_GetString(objv[i+2]);
00589         target = Tcl_GetSlave(interp, slaveIntName);
00590         if (target == NULL) {
00591             return TCL_ERROR;
00592         }
00593     }
00594 
00595     /*
00596      * Scan through the packages that are currently loaded to see if the
00597      * package we want is already loaded. We'll use a loaded package if it
00598      * meets any of the following conditions:
00599      *  - Its name and file match the once we're looking for.
00600      *  - Its file matches, and we weren't given a name.
00601      *  - Its name matches, the file name was specified as empty, and there is
00602      *    only no statically loaded package with the same name.
00603      */
00604 
00605     Tcl_MutexLock(&packageMutex);
00606 
00607     defaultPtr = NULL;
00608     for (pkgPtr = firstPackagePtr; pkgPtr != NULL; pkgPtr = pkgPtr->nextPtr) {
00609         int namesMatch, filesMatch;
00610 
00611         if (packageName == NULL) {
00612             namesMatch = 0;
00613         } else {
00614             Tcl_DStringSetLength(&pkgName, 0);
00615             Tcl_DStringAppend(&pkgName, packageName, -1);
00616             Tcl_DStringSetLength(&tmp, 0);
00617             Tcl_DStringAppend(&tmp, pkgPtr->packageName, -1);
00618             Tcl_UtfToLower(Tcl_DStringValue(&pkgName));
00619             Tcl_UtfToLower(Tcl_DStringValue(&tmp));
00620             if (strcmp(Tcl_DStringValue(&tmp),
00621                     Tcl_DStringValue(&pkgName)) == 0) {
00622                 namesMatch = 1;
00623             } else {
00624                 namesMatch = 0;
00625             }
00626         }
00627         Tcl_DStringSetLength(&pkgName, 0);
00628 
00629         filesMatch = (strcmp(pkgPtr->fileName, fullFileName) == 0);
00630         if (filesMatch && (namesMatch || (packageName == NULL))) {
00631             break;
00632         }
00633         if (namesMatch && (fullFileName[0] == 0)) {
00634             defaultPtr = pkgPtr;
00635         }
00636         if (filesMatch && !namesMatch && (fullFileName[0] != 0)) {
00637             break;
00638         }
00639     }
00640     Tcl_MutexUnlock(&packageMutex);
00641     if (fullFileName[0] == 0) {
00642         /*
00643          * It's an error to try unload a static package.
00644          */
00645 
00646         Tcl_AppendResult(interp, "package \"", packageName,
00647                 "\" is loaded statically and cannot be unloaded", NULL);
00648         code = TCL_ERROR;
00649         goto done;
00650     }
00651     if (pkgPtr == NULL) {
00652         /*
00653          * The DLL pointed by the provided filename has never been loaded.
00654          */
00655 
00656         Tcl_AppendResult(interp, "file \"", fullFileName,
00657                 "\" has never been loaded", NULL);
00658         code = TCL_ERROR;
00659         goto done;
00660     }
00661 
00662     /*
00663      * Scan through the list of packages already loaded in the target
00664      * interpreter. If the package we want is already loaded there, then we
00665      * should proceed with unloading.
00666      */
00667 
00668     code = TCL_ERROR;
00669     if (pkgPtr != NULL) {
00670         ipFirstPtr = (InterpPackage *) Tcl_GetAssocData(target,
00671                 "tclLoad", NULL);
00672         for (ipPtr = ipFirstPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
00673             if (ipPtr->pkgPtr == pkgPtr) {
00674                 code = TCL_OK;
00675                 break;
00676             }
00677         }
00678     }
00679     if (code != TCL_OK) {
00680         /*
00681          * The package has not been loaded in this interpreter.
00682          */
00683 
00684         Tcl_AppendResult(interp, "file \"", fullFileName,
00685                 "\" has never been loaded in this interpreter", NULL);
00686         code = TCL_ERROR;
00687         goto done;
00688     }
00689 
00690     /*
00691      * Ensure that the DLL can be unloaded. If it is a trusted interpreter,
00692      * pkgPtr->unloadProc must not be NULL for the DLL to be unloadable. If
00693      * the interpreter is a safe one, pkgPtr->safeUnloadProc must be non-NULL.
00694      */
00695 
00696     if (Tcl_IsSafe(target)) {
00697         if (pkgPtr->safeUnloadProc == NULL) {
00698             Tcl_AppendResult(interp, "file \"", fullFileName,
00699                     "\" cannot be unloaded under a safe interpreter", NULL);
00700             code = TCL_ERROR;
00701             goto done;
00702         }
00703         unloadProc = pkgPtr->safeUnloadProc;
00704     } else {
00705         if (pkgPtr->unloadProc == NULL) {
00706             Tcl_AppendResult(interp, "file \"", fullFileName,
00707                     "\" cannot be unloaded under a trusted interpreter", NULL);
00708             code = TCL_ERROR;
00709             goto done;
00710         }
00711         unloadProc = pkgPtr->unloadProc;
00712     }
00713 
00714     /*
00715      * We are ready to unload the package. First, evaluate the unload
00716      * function. If this fails, we cannot proceed with unload. Also, we must
00717      * specify the proper flag to pass to the unload callback.
00718      * TCL_UNLOAD_DETACH_FROM_INTERPRETER is defined when the callback should
00719      * only remove itself from the interpreter; the library will be unloaded
00720      * in a future call of unload. In case the library will be unloaded just
00721      * after the callback returns, TCL_UNLOAD_DETACH_FROM_PROCESS is passed.
00722      */
00723 
00724     code = TCL_UNLOAD_DETACH_FROM_INTERPRETER;
00725     if (!keepLibrary) {
00726         Tcl_MutexLock(&packageMutex);
00727         trustedRefCount = pkgPtr->interpRefCount;
00728         safeRefCount = pkgPtr->safeInterpRefCount;
00729         Tcl_MutexUnlock(&packageMutex);
00730 
00731         if (Tcl_IsSafe(target)) {
00732             --safeRefCount;
00733         } else {
00734             --trustedRefCount;
00735         }
00736 
00737         if (safeRefCount <= 0 && trustedRefCount <= 0) {
00738             code = TCL_UNLOAD_DETACH_FROM_PROCESS;
00739         }
00740     }
00741     code = (*unloadProc)(target, code);
00742     if (code != TCL_OK) {
00743         TclTransferResult(target, code, interp);
00744         goto done;
00745     }
00746 
00747     /*
00748      * The unload function executed fine. Examine the reference count to see
00749      * if we unload the DLL.
00750      */
00751 
00752     Tcl_MutexLock(&packageMutex);
00753     if (Tcl_IsSafe(target)) {
00754         --pkgPtr->safeInterpRefCount;
00755 
00756         /*
00757          * Do not let counter get negative.
00758          */
00759 
00760         if (pkgPtr->safeInterpRefCount < 0) {
00761             pkgPtr->safeInterpRefCount = 0;
00762         }
00763     } else {
00764         --pkgPtr->interpRefCount;
00765 
00766         /*
00767          * Do not let counter get negative.
00768          */
00769 
00770         if (pkgPtr->interpRefCount < 0) {
00771             pkgPtr->interpRefCount = 0;
00772         }
00773     }
00774     trustedRefCount = pkgPtr->interpRefCount;
00775     safeRefCount = pkgPtr->safeInterpRefCount;
00776     Tcl_MutexUnlock(&packageMutex);
00777 
00778     code = TCL_OK;
00779     if (pkgPtr->safeInterpRefCount <= 0 && pkgPtr->interpRefCount <= 0
00780             && !keepLibrary) {
00781         /*
00782          * Unload the shared library from the application memory...
00783          */
00784 
00785 #if defined(TCL_UNLOAD_DLLS) || defined(__WIN32__)
00786         /*
00787          * Some Unix dlls are poorly behaved - registering things like atexit
00788          * calls that can't be unregistered. If you unload such dlls, you get
00789          * a core on exit because it wants to call a function in the dll after
00790          * it's been unloaded.
00791          */
00792 
00793         if (pkgPtr->fileName[0] != '\0') {
00794             Tcl_FSUnloadFileProc *unLoadProcPtr = pkgPtr->unLoadProcPtr;
00795 
00796             if (unLoadProcPtr != NULL) {
00797                 Tcl_MutexLock(&packageMutex);
00798                 (*unLoadProcPtr)(pkgPtr->loadHandle);
00799 
00800                 /*
00801                  * Remove this library from the loaded library cache.
00802                  */
00803 
00804                 defaultPtr = pkgPtr;
00805                 if (defaultPtr == firstPackagePtr) {
00806                     firstPackagePtr = pkgPtr->nextPtr;
00807                 } else {
00808                     for (pkgPtr = firstPackagePtr; pkgPtr != NULL;
00809                             pkgPtr = pkgPtr->nextPtr) {
00810                         if (pkgPtr->nextPtr == defaultPtr) {
00811                             pkgPtr->nextPtr = defaultPtr->nextPtr;
00812                             break;
00813                         }
00814                     }
00815                 }
00816 
00817                 /*
00818                  * Remove this library from the interpreter's library cache.
00819                  */
00820 
00821                 ipFirstPtr = (InterpPackage *) Tcl_GetAssocData(target,
00822                         "tclLoad", NULL);
00823                 ipPtr = ipFirstPtr;
00824                 if (ipPtr->pkgPtr == defaultPtr) {
00825                     ipFirstPtr = ipFirstPtr->nextPtr;
00826                 } else {
00827                     InterpPackage *ipPrevPtr;
00828 
00829                     for (ipPrevPtr = ipPtr; ipPtr != NULL;
00830                             ipPrevPtr = ipPtr, ipPtr = ipPtr->nextPtr) {
00831                         if (ipPtr->pkgPtr == pkgPtr) {
00832                             ipPrevPtr->nextPtr = ipPtr->nextPtr;
00833                             break;
00834                         }
00835                     }
00836                 }
00837                 Tcl_SetAssocData(target, "tclLoad", LoadCleanupProc,
00838                         (ClientData) ipFirstPtr);
00839                 ckfree(defaultPtr->fileName);
00840                 ckfree(defaultPtr->packageName);
00841                 ckfree((char *) defaultPtr);
00842                 ckfree((char *) ipPtr);
00843                 Tcl_MutexUnlock(&packageMutex);
00844             } else {
00845                 Tcl_AppendResult(interp, "file \"", fullFileName,
00846                         "\" cannot be unloaded: filesystem does not support unloading",
00847                         NULL);
00848                 code = TCL_ERROR;
00849             }
00850         }
00851 #else
00852         Tcl_AppendResult(interp, "file \"", fullFileName,
00853                 "\" cannot be unloaded: unloading disabled", NULL);
00854         code = TCL_ERROR;
00855 #endif
00856     }
00857 
00858   done:
00859     Tcl_DStringFree(&pkgName);
00860     Tcl_DStringFree(&tmp);
00861     if (!complain && code!=TCL_OK) {
00862         code = TCL_OK;
00863         Tcl_ResetResult(interp);
00864     }
00865     if (code == TCL_OK) {
00866 #if 0
00867         /*
00868          * Result of [unload] was not documented in TIP#100, so force to be
00869          * the empty string by commenting this out. DKF.
00870          */
00871 
00872         Tcl_Obj *resultObjPtr, *objPtr[2];
00873 
00874         /*
00875          * Our result is the two reference counts.
00876          */
00877 
00878         objPtr[0] = Tcl_NewIntObj(trustedRefCount);
00879         objPtr[1] = Tcl_NewIntObj(safeRefCount);
00880         if (objPtr[0] == NULL || objPtr[1] == NULL) {
00881             if (objPtr[0]) {
00882                 Tcl_DecrRefCount(objPtr[0]);
00883             }
00884             if (objPtr[1]) {
00885                 Tcl_DecrRefCount(objPtr[1]);
00886             }
00887         } else {
00888             resultObjPtr = Tcl_NewListObj(2, objPtr);
00889             if (resultObjPtr != NULL) {
00890                 Tcl_SetObjResult(interp, resultObjPtr);
00891             }
00892         }
00893 #endif
00894     }
00895     return code;
00896 }
00897 
00898 /*
00899  *----------------------------------------------------------------------
00900  *
00901  * Tcl_StaticPackage --
00902  *
00903  *      This function is invoked to indicate that a particular package has
00904  *      been linked statically with an application.
00905  *
00906  * Results:
00907  *      None.
00908  *
00909  * Side effects:
00910  *      Once this function completes, the package becomes loadable via the
00911  *      "load" command with an empty file name.
00912  *
00913  *----------------------------------------------------------------------
00914  */
00915 
00916 void
00917 Tcl_StaticPackage(
00918     Tcl_Interp *interp,         /* If not NULL, it means that the package has
00919                                  * already been loaded into the given
00920                                  * interpreter by calling the appropriate init
00921                                  * proc. */
00922     const char *pkgName,        /* Name of package (must be properly
00923                                  * capitalized: first letter upper case,
00924                                  * others lower case). */
00925     Tcl_PackageInitProc *initProc,
00926                                 /* Function to call to incorporate this
00927                                  * package into a trusted interpreter. */
00928     Tcl_PackageInitProc *safeInitProc)
00929                                 /* Function to call to incorporate this
00930                                  * package into a safe interpreter (one that
00931                                  * will execute untrusted scripts). NULL means
00932                                  * the package can't be used in safe
00933                                  * interpreters. */
00934 {
00935     LoadedPackage *pkgPtr;
00936     InterpPackage *ipPtr, *ipFirstPtr;
00937 
00938     /*
00939      * Check to see if someone else has already reported this package as
00940      * statically loaded in the process.
00941      */
00942 
00943     Tcl_MutexLock(&packageMutex);
00944     for (pkgPtr = firstPackagePtr; pkgPtr != NULL; pkgPtr = pkgPtr->nextPtr) {
00945         if ((pkgPtr->initProc == initProc)
00946                 && (pkgPtr->safeInitProc == safeInitProc)
00947                 && (strcmp(pkgPtr->packageName, pkgName) == 0)) {
00948             break;
00949         }
00950     }
00951     Tcl_MutexUnlock(&packageMutex);
00952 
00953     /*
00954      * If the package is not yet recorded as being loaded statically, add it
00955      * to the list now.
00956      */
00957 
00958     if ( pkgPtr == NULL ) {
00959         pkgPtr = (LoadedPackage *) ckalloc(sizeof(LoadedPackage));
00960         pkgPtr->fileName        = (char *) ckalloc((unsigned) 1);
00961         pkgPtr->fileName[0]     = 0;
00962         pkgPtr->packageName     = (char *)
00963                 ckalloc((unsigned) (strlen(pkgName) + 1));
00964         strcpy(pkgPtr->packageName, pkgName);
00965         pkgPtr->loadHandle      = NULL;
00966         pkgPtr->initProc        = initProc;
00967         pkgPtr->safeInitProc    = safeInitProc;
00968         Tcl_MutexLock(&packageMutex);
00969         pkgPtr->nextPtr         = firstPackagePtr;
00970         firstPackagePtr         = pkgPtr;
00971         Tcl_MutexUnlock(&packageMutex);
00972     }
00973 
00974     if (interp != NULL) {
00975 
00976         /*
00977          * If we're loading the package into an interpreter, determine whether
00978          * it's already loaded.
00979          */
00980 
00981         ipFirstPtr = (InterpPackage *) Tcl_GetAssocData(interp,
00982                 "tclLoad", NULL);
00983         for ( ipPtr = ipFirstPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr ) {
00984             if ( ipPtr->pkgPtr == pkgPtr ) {
00985                 return;
00986             }
00987         }
00988 
00989         /*
00990          * Package isn't loade in the current interp yet. Mark it as now being
00991          * loaded.
00992          */
00993 
00994         ipPtr = (InterpPackage *) ckalloc(sizeof(InterpPackage));
00995         ipPtr->pkgPtr = pkgPtr;
00996         ipPtr->nextPtr = ipFirstPtr;
00997         Tcl_SetAssocData(interp, "tclLoad", LoadCleanupProc,
00998                 (ClientData) ipPtr);
00999     }
01000 }
01001 
01002 /*
01003  *----------------------------------------------------------------------
01004  *
01005  * TclGetLoadedPackages --
01006  *
01007  *      This function returns information about all of the files that are
01008  *      loaded (either in a particular intepreter, or for all interpreters).
01009  *
01010  * Results:
01011  *      The return value is a standard Tcl completion code. If successful, a
01012  *      list of lists is placed in the interp's result. Each sublist
01013  *      corresponds to one loaded file; its first element is the name of the
01014  *      file (or an empty string for something that's statically loaded) and
01015  *      the second element is the name of the package in that file.
01016  *
01017  * Side effects:
01018  *      None.
01019  *
01020  *----------------------------------------------------------------------
01021  */
01022 
01023 int
01024 TclGetLoadedPackages(
01025     Tcl_Interp *interp,         /* Interpreter in which to return information
01026                                  * or error message. */
01027     char *targetName)           /* Name of target interpreter or NULL. If
01028                                  * NULL, return info about all interps;
01029                                  * otherwise, just return info about this
01030                                  * interpreter. */
01031 {
01032     Tcl_Interp *target;
01033     LoadedPackage *pkgPtr;
01034     InterpPackage *ipPtr;
01035     const char *prefix;
01036 
01037     if (targetName == NULL) {
01038         /*
01039          * Return information about all of the available packages.
01040          */
01041 
01042         prefix = "{";
01043         Tcl_MutexLock(&packageMutex);
01044         for (pkgPtr = firstPackagePtr; pkgPtr != NULL;
01045                 pkgPtr = pkgPtr->nextPtr) {
01046             Tcl_AppendResult(interp, prefix, NULL);
01047             Tcl_AppendElement(interp, pkgPtr->fileName);
01048             Tcl_AppendElement(interp, pkgPtr->packageName);
01049             Tcl_AppendResult(interp, "}", NULL);
01050             prefix = " {";
01051         }
01052         Tcl_MutexUnlock(&packageMutex);
01053         return TCL_OK;
01054     }
01055 
01056     /*
01057      * Return information about only the packages that are loaded in a given
01058      * interpreter.
01059      */
01060 
01061     target = Tcl_GetSlave(interp, targetName);
01062     if (target == NULL) {
01063         return TCL_ERROR;
01064     }
01065     ipPtr = (InterpPackage *) Tcl_GetAssocData(target, "tclLoad", NULL);
01066     prefix = "{";
01067     for ( ; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
01068         pkgPtr = ipPtr->pkgPtr;
01069         Tcl_AppendResult(interp, prefix, NULL);
01070         Tcl_AppendElement(interp, pkgPtr->fileName);
01071         Tcl_AppendElement(interp, pkgPtr->packageName);
01072         Tcl_AppendResult(interp, "}", NULL);
01073         prefix = " {";
01074     }
01075     return TCL_OK;
01076 }
01077 
01078 /*
01079  *----------------------------------------------------------------------
01080  *
01081  * LoadCleanupProc --
01082  *
01083  *      This function is called to delete all of the InterpPackage structures
01084  *      for an interpreter when the interpreter is deleted. It gets invoked
01085  *      via the Tcl AssocData mechanism.
01086  *
01087  * Results:
01088  *      None.
01089  *
01090  * Side effects:
01091  *      Storage for all of the InterpPackage functions for interp get deleted.
01092  *
01093  *----------------------------------------------------------------------
01094  */
01095 
01096 static void
01097 LoadCleanupProc(
01098     ClientData clientData,      /* Pointer to first InterpPackage structure
01099                                  * for interp. */
01100     Tcl_Interp *interp)         /* Interpreter that is being deleted. */
01101 {
01102     InterpPackage *ipPtr, *nextPtr;
01103 
01104     ipPtr = (InterpPackage *) clientData;
01105     while (ipPtr != NULL) {
01106         nextPtr = ipPtr->nextPtr;
01107         ckfree((char *) ipPtr);
01108         ipPtr = nextPtr;
01109     }
01110 }
01111 
01112 /*
01113  *----------------------------------------------------------------------
01114  *
01115  * TclFinalizeLoad --
01116  *
01117  *      This function is invoked just before the application exits. It frees
01118  *      all of the LoadedPackage structures.
01119  *
01120  * Results:
01121  *      None.
01122  *
01123  * Side effects:
01124  *      Memory is freed.
01125  *
01126  *----------------------------------------------------------------------
01127  */
01128 
01129 void
01130 TclFinalizeLoad(void)
01131 {
01132     LoadedPackage *pkgPtr;
01133 
01134     /*
01135      * No synchronization here because there should just be one thread alive
01136      * at this point. Logically, packageMutex should be grabbed at this point,
01137      * but the Mutexes get finalized before the call to this routine. The
01138      * only subsystem left alive at this point is the memory allocator.
01139      */
01140 
01141     while (firstPackagePtr != NULL) {
01142         pkgPtr = firstPackagePtr;
01143         firstPackagePtr = pkgPtr->nextPtr;
01144 
01145 #if defined(TCL_UNLOAD_DLLS) || defined(__WIN32__)
01146         /*
01147          * Some Unix dlls are poorly behaved - registering things like atexit
01148          * calls that can't be unregistered. If you unload such dlls, you get
01149          * a core on exit because it wants to call a function in the dll after
01150          * it has been unloaded.
01151          */
01152 
01153         if (pkgPtr->fileName[0] != '\0') {
01154             Tcl_FSUnloadFileProc *unLoadProcPtr = pkgPtr->unLoadProcPtr;
01155             if (unLoadProcPtr != NULL) {
01156                 (*unLoadProcPtr)(pkgPtr->loadHandle);
01157             }
01158         }
01159 #endif
01160 
01161         ckfree(pkgPtr->fileName);
01162         ckfree(pkgPtr->packageName);
01163         ckfree((char *) pkgPtr);
01164     }
01165 }
01166 
01167 /*
01168  * Local Variables:
01169  * mode: c
01170  * c-basic-offset: 4
01171  * fill-column: 78
01172  * End:
01173  */



Generated on Wed Mar 12 12:18:19 2008 by  doxygen 1.5.1