/* * tclPkg.c -- * * This file implements package and version control for Tcl via the * "package" command and a few C APIs. * * Copyright (c) 1996 Sun Microsystems, Inc. * Copyright (c) 2006 Andreas Kupries * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * * TIP #268. * Heavily rewritten to handle the extend version numbers, and extended * package requirements. */ #include "tclInt.h" /* * Each invocation of the "package ifneeded" command creates a structure of * the following type, which is used to load the package into the interpreter * if it is requested with a "package require" command. */ typedef struct PkgAvail { char *version; /* Version string; malloc'ed. */ char *script; /* Script to invoke to provide this version of * the package. Malloc'ed and protected by * Tcl_Preserve and Tcl_Release. */ struct PkgAvail *nextPtr; /* Next in list of available versions of the * same package. */ } PkgAvail; /* * For each package that is known in any way to an interpreter, there is one * record of the following type. These records are stored in the * "packageTable" hash table in the interpreter, keyed by package name such as * "Tk" (no version number). */ typedef struct Package { char *version; /* Version that has been supplied in this * interpreter via "package provide" * (malloc'ed). NULL means the package doesn't * exist in this interpreter yet. */ PkgAvail *availPtr; /* First in list of all available versions of * this package. */ const void *clientData; /* Client data. */ } Package; /* * Prototypes for functions defined in this file: */ static int CheckVersionAndConvert(Tcl_Interp *interp, const char *string, char **internal, int *stable); static int CompareVersions(char *v1i, char *v2i, int *isMajorPtr); static int CheckRequirement(Tcl_Interp *interp, const char *string); static int CheckAllRequirements(Tcl_Interp *interp, int reqc, Tcl_Obj *const reqv[]); static int RequirementSatisfied(char *havei, const char *req); static int SomeRequirementSatisfied(char *havei, int reqc, Tcl_Obj *const reqv[]); static void AddRequirementsToResult(Tcl_Interp *interp, int reqc, Tcl_Obj *const reqv[]); static void AddRequirementsToDString(Tcl_DString *dstring, int reqc, Tcl_Obj *const reqv[]); static Package * FindPackage(Tcl_Interp *interp, const char *name); static const char * PkgRequireCore(Tcl_Interp *interp, const char *name, int reqc, Tcl_Obj *const reqv[], void *clientDataPtr); /* * Helper macros. */ #define DupBlock(v,s,len) \ ((v) = ckalloc(len), memcpy((v),(s),(len))) #define DupString(v,s) \ do { \ unsigned local__len = (unsigned) (strlen(s) + 1); \ DupBlock((v),(s),local__len); \ } while (0) /* *---------------------------------------------------------------------- * * Tcl_PkgProvide / Tcl_PkgProvideEx -- * * This function is invoked to declare that a particular version of a * particular package is now present in an interpreter. There must not be * any other version of this package already provided in the interpreter. * * Results: * Normally returns TCL_OK; if there is already another version of the * package loaded then TCL_ERROR is returned and an error message is left * in the interp's result. * * Side effects: * The interpreter remembers that this package is available, so that no * other version of the package may be provided for the interpreter. * *---------------------------------------------------------------------- */ #undef Tcl_PkgProvide int Tcl_PkgProvide( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ const char *name, /* Name of package. */ const char *version) /* Version string for package. */ { return Tcl_PkgProvideEx(interp, name, version, NULL); } int Tcl_PkgProvideEx( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ const char *name, /* Name of package. */ const char *version, /* Version string for package. */ const void *clientData) /* clientdata for this package (normally used * for C callback function table) */ { Package *pkgPtr; char *pvi, *vi; int res; pkgPtr = FindPackage(interp, name); if (pkgPtr->version == NULL) { DupString(pkgPtr->version, version); pkgPtr->clientData = clientData; return TCL_OK; } if (CheckVersionAndConvert(interp, pkgPtr->version, &pvi, NULL) != TCL_OK) { return TCL_ERROR; } else if (CheckVersionAndConvert(interp, version, &vi, NULL) != TCL_OK) { ckfree(pvi); return TCL_ERROR; } res = CompareVersions(pvi, vi, NULL); ckfree(pvi); ckfree(vi); if (res == 0) { if (clientData != NULL) { pkgPtr->clientData = clientData; } return TCL_OK; } Tcl_SetObjResult(interp, Tcl_ObjPrintf( "conflicting versions provided for package \"%s\": %s, then %s", name, pkgPtr->version, version)); Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "VERSIONCONFLICT", NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * Tcl_PkgRequire / Tcl_PkgRequireEx / Tcl_PkgRequireProc -- * * This function is called by code that depends on a particular version * of a particular package. If the package is not already provided in the * interpreter, this function invokes a Tcl script to provide it. If the * package is already provided, this function makes sure that the * caller's needs don't conflict with the version that is present. * * Results: * If successful, returns the version string for the currently provided * version of the package, which may be different from the "version" * argument. If the caller's requirements cannot be met (e.g. the version * requested conflicts with a currently provided version, or the required * version cannot be found, or the script to provide the required version * generates an error), NULL is returned and an error message is left in * the interp's result. * * Side effects: * The script from some previous "package ifneeded" command may be * invoked to provide the package. * *---------------------------------------------------------------------- */ #undef Tcl_PkgRequire const char * Tcl_PkgRequire( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ const char *name, /* Name of desired package. */ const char *version, /* Version string for desired version; NULL * means use the latest version available. */ int exact) /* Non-zero means that only the particular * version given is acceptable. Zero means use * the latest compatible version. */ { return Tcl_PkgRequireEx(interp, name, version, exact, NULL); } const char * Tcl_PkgRequireEx( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ const char *name, /* Name of desired package. */ const char *version, /* Version string for desired version; NULL * means use the latest version available. */ int exact, /* Non-zero means that only the particular * version given is acceptable. Zero means use * the latest compatible version. */ void *clientDataPtr) /* Used to return the client data for this * package. If it is NULL then the client data * is not returned. This is unchanged if this * call fails for any reason. */ { Tcl_Obj *ov; const char *result = NULL; /* * If an attempt is being made to load this into a standalone executable * on a platform where backlinking is not supported then this must be a * shared version of Tcl (Otherwise the load would have failed). Detect * this situation by checking that this library has been correctly * initialised. If it has not been then return immediately as nothing will * work. */ if (tclEmptyStringRep == NULL) { /* * OK, so what's going on here? * * First, what are we doing? We are performing a check on behalf of * one particular caller, Tcl_InitStubs(). When a package is stub- * enabled, it is statically linked to libtclstub.a, which contains a * copy of Tcl_InitStubs(). When a stub-enabled package is loaded, its * *_Init() function is supposed to call Tcl_InitStubs() before * calling any other functions in the Tcl library. The first Tcl * function called by Tcl_InitStubs() through the stub table is * Tcl_PkgRequireEx(), so this code right here is the first code that * is part of the original Tcl library in the executable that gets * executed on behalf of a newly loaded stub-enabled package. * * One easy error for the developer/builder of a stub-enabled package * to make is to forget to define USE_TCL_STUBS when compiling the * package. When that happens, the package will contain symbols that * are references to the Tcl library, rather than function pointers * referencing the stub table. On platforms that lack backlinking, * those unresolved references may cause the loading of the package to * also load a second copy of the Tcl library, leading to all kinds of * trouble. We would like to catch that error and report a useful * message back to the user. That's what we're doing. * * Second, how does this work? If we reach this point, then the global * variable tclEmptyStringRep has the value NULL. Compare that with * the definition of tclEmptyStringRep near the top of the file * generic/tclObj.c. It clearly should not have the value NULL; it * should point to the char tclEmptyString. If we see it having the * value NULL, then somehow we are seeing a Tcl library that isn't * completely initialized, and that's an indicator for the error * condition described above. (Further explanation is welcome.) * * Third, so what do we do about it? This situation indicates the * package we just loaded wasn't properly compiled to be stub-enabled, * yet it thinks it is stub-enabled (it called Tcl_InitStubs()). We * want to report that the package just loaded is broken, so we want * to place an error message in the interpreter result and return NULL * to indicate failure to Tcl_InitStubs() so that it will also fail. * (Further explanation why we don't want to Tcl_Panic() is welcome. * After all, two Tcl libraries can't be a good thing!) * * Trouble is that's going to be tricky. We're now using a Tcl library * that's not fully initialized. In particular, it doesn't have a * proper value for tclEmptyStringRep. The Tcl_Obj system heavily * depends on the value of tclEmptyStringRep and all of Tcl depends * (increasingly) on the Tcl_Obj system, we need to correct that flaw * before making the calls to set the interpreter result to the error * message. That's the only flaw corrected; other problems with * initialization of the Tcl library are not remedied, so be very * careful about adding any other calls here without checking how they * behave when initialization is incomplete. */ tclEmptyStringRep = &tclEmptyString; Tcl_SetObjResult(interp, Tcl_ObjPrintf( "Cannot load package \"%s\" in standalone executable:" " This package is not compiled with stub support", name)); Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "UNSTUBBED", NULL); return NULL; } /* * Translate between old and new API, and defer to the new function. */ if (version == NULL) { result = PkgRequireCore(interp, name, 0, NULL, clientDataPtr); } else { if (exact && TCL_OK != CheckVersionAndConvert(interp, version, NULL, NULL)) { return NULL; } ov = Tcl_NewStringObj(version, -1); if (exact) { Tcl_AppendStringsToObj(ov, "-", version, NULL); } Tcl_IncrRefCount(ov); result = PkgRequireCore(interp, name, 1, &ov, clientDataPtr); TclDecrRefCount(ov); } return result; } int Tcl_PkgRequireProc( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ const char *name, /* Name of desired package. */ int reqc, /* Requirements constraining the desired * version. */ Tcl_Obj *const reqv[], /* 0 means to use the latest version * available. */ void *clientDataPtr) { const char *result = PkgRequireCore(interp, name, reqc, reqv, clientDataPtr); if (result == NULL) { return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewStringObj(result, -1)); return TCL_OK; } static const char * PkgRequireCore( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ const char *name, /* Name of desired package. */ int reqc, /* Requirements constraining the desired * version. */ Tcl_Obj *const reqv[], /* 0 means to use the latest version * available. */ void *clientDataPtr) { Interp *iPtr = (Interp *) interp; Package *pkgPtr; PkgAvail *availPtr, *bestPtr, *bestStablePtr; char *availVersion, *bestVersion; /* Internal rep. of versions */ int availStable, code, satisfies, pass; char *script, *pkgVersionI; Tcl_DString command; if (TCL_OK != CheckAllRequirements(interp, reqc, reqv)) { return NULL; } /* * It can take up to three passes to find the package: one pass to run the * "package unknown" script, one to run the "package ifneeded" script for * a specific version, and a final pass to lookup the package loaded by * the "package ifneeded" script. */ for (pass=1 ;; pass++) { pkgPtr = FindPackage(interp, name); if (pkgPtr->version != NULL) { break; } /* * Check whether we're already attempting to load some version of this * package (circular dependency detection). */ if (pkgPtr->clientData != NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "circular package dependency:" " attempt to provide %s %s requires %s", name, (char *) pkgPtr->clientData, name)); AddRequirementsToResult(interp, reqc, reqv); Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "CIRCULARITY", NULL); return NULL; } /* * The package isn't yet present. Search the list of available * versions and invoke the script for the best available version. We * are actually locating the best, and the best stable version. One of * them is then chosen based on the selection mode. */ bestPtr = NULL; bestStablePtr = NULL; bestVersion = NULL; for (availPtr = pkgPtr->availPtr; availPtr != NULL; availPtr = availPtr->nextPtr) { if (CheckVersionAndConvert(interp, availPtr->version, &availVersion, &availStable) != TCL_OK) { /* * The provided version number has invalid syntax. This * should not happen. This should have been caught by the * 'package ifneeded' registering the package. */ continue; } if (bestPtr != NULL) { int res = CompareVersions(availVersion, bestVersion, NULL); /* * Note: Use internal reps! */ if (res <= 0) { /* * The version of the package sought is not as good as the * currently selected version. Ignore it. */ ckfree(availVersion); availVersion = NULL; continue; } } /* * We have found a version which is better than our max. */ if (reqc > 0) { /* Check satisfaction of requirements. */ satisfies = SomeRequirementSatisfied(availVersion, reqc, reqv); if (!satisfies) { ckfree(availVersion); availVersion = NULL; continue; } } bestPtr = availPtr; if (bestVersion != NULL) { ckfree(bestVersion); } bestVersion = availVersion; /* * If this new best version is stable then it also has to be * better than the max stable version found so far. */ if (availStable) { bestStablePtr = availPtr; } } if (bestVersion != NULL) { ckfree(bestVersion); } /* * Now choose a version among the two best. For 'latest' we simply * take (actually keep) the best. For 'stable' we take the best * stable, if there is any, or the best if there is nothing stable. */ if ((iPtr->packagePrefer == PKG_PREFER_STABLE) && (bestStablePtr != NULL)) { bestPtr = bestStablePtr; } if (bestPtr != NULL) { /* * We found an ifneeded script for the package. Be careful while * executing it: this could cause reentrancy, so (a) protect the * script itself from deletion and (b) don't assume that bestPtr * will still exist when the script completes. */ char *versionToProvide = bestPtr->version; script = bestPtr->script; pkgPtr->clientData = versionToProvide; Tcl_Preserve(script); Tcl_Preserve(versionToProvide); code = Tcl_EvalEx(interp, script, -1, TCL_EVAL_GLOBAL); Tcl_Release(script); pkgPtr = FindPackage(interp, name); if (code == TCL_OK) { Tcl_ResetResult(interp); if (pkgPtr->version == NULL) { code = TCL_ERROR; Tcl_SetObjResult(interp, Tcl_ObjPrintf( "attempt to provide package %s %s failed:" " no version of package %s provided", name, versionToProvide, name)); Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "UNPROVIDED", NULL); } else { char *pvi, *vi; if (CheckVersionAndConvert(interp, pkgPtr->version, &pvi, NULL) != TCL_OK) { code = TCL_ERROR; } else if (CheckVersionAndConvert(interp, versionToProvide, &vi, NULL) != TCL_OK) { ckfree(pvi); code = TCL_ERROR; } else { int res = CompareVersions(pvi, vi, NULL); ckfree(pvi); ckfree(vi); if (res != 0) { code = TCL_ERROR; Tcl_SetObjResult(interp, Tcl_ObjPrintf( "attempt to provide package %s %s failed:" " package %s %s provided instead", name, versionToProvide, name, pkgPtr->version)); Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "WRONGPROVIDE", NULL); } } } } else if (code != TCL_ERROR) { Tcl_Obj *codePtr = Tcl_NewIntObj(code); Tcl_SetObjResult(interp, Tcl_ObjPrintf( "attempt to provide package %s %s failed:" " bad return code: %s", name, versionToProvide, TclGetString(codePtr))); Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "BADRESULT", NULL); TclDecrRefCount(codePtr); code = TCL_ERROR; } if (code == TCL_ERROR) { Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (\"package ifneeded %s %s\" script)", name, versionToProvide)); } Tcl_Release(versionToProvide); if (code != TCL_OK) { /* * Take a non-TCL_OK code from the script as an indication the * package wasn't loaded properly, so the package system * should not remember an improper load. * * This is consistent with our returning NULL. If we're not * willing to tell our caller we got a particular version, we * shouldn't store that version for telling future callers * either. */ if (pkgPtr->version != NULL) { ckfree(pkgPtr->version); pkgPtr->version = NULL; } pkgPtr->clientData = NULL; return NULL; } break; } /* * The package is not in the database. If there is a "package unknown" * command, invoke it (but only on the first pass; after that, we * should not get here in the first place). */ if (pass > 1) { break; } script = ((Interp *) interp)->packageUnknown; if (script != NULL) { Tcl_DStringInit(&command); Tcl_DStringAppend(&command, script, -1); Tcl_DStringAppendElement(&command, name); AddRequirementsToDString(&command, reqc, reqv); code = Tcl_EvalEx(interp, Tcl_DStringValue(&command), Tcl_DStringLength(&command), TCL_EVAL_GLOBAL); Tcl_DStringFree(&command); if ((code != TCL_OK) && (code != TCL_ERROR)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "bad return code: %d", code)); Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "BADRESULT", NULL); code = TCL_ERROR; } if (code == TCL_ERROR) { Tcl_AddErrorInfo(interp, "\n (\"package unknown\" script)"); return NULL; } Tcl_ResetResult(interp); } } if (pkgPtr->version == NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "can't find package %s", name)); Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "UNFOUND", NULL); AddRequirementsToResult(interp, reqc, reqv); return NULL; } /* * At this point we know that the package is present. Make sure that the * provided version meets the current requirements. */ if (reqc != 0) { CheckVersionAndConvert(interp, pkgPtr->version, &pkgVersionI, NULL); satisfies = SomeRequirementSatisfied(pkgVersionI, reqc, reqv); ckfree(pkgVersionI); if (!satisfies) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "version conflict for package \"%s\": have %s, need", name, pkgPtr->version)); Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "VERSIONCONFLICT", NULL); AddRequirementsToResult(interp, reqc, reqv); return NULL; } } if (clientDataPtr) { const void **ptr = (const void **) clientDataPtr; *ptr = pkgPtr->clientData; } return pkgPtr->version; } /* *---------------------------------------------------------------------- * * Tcl_PkgPresent / Tcl_PkgPresentEx -- * * Checks to see whether the specified package is present. If it is not * then no additional action is taken. * * Results: * If successful, returns the version string for the currently provided * version of the package, which may be different from the "version" * argument. If the caller's requirements cannot be met (e.g. the version * requested conflicts with a currently provided version), NULL is * returned and an error message is left in interp->result. * * Side effects: * None. * *---------------------------------------------------------------------- */ #undef Tcl_PkgPresent const char * Tcl_PkgPresent( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ const char *name, /* Name of desired package. */ const char *version, /* Version string for desired version; NULL * means use the latest version available. */ int exact) /* Non-zero means that only the particular * version given is acceptable. Zero means use * the latest compatible version. */ { return Tcl_PkgPresentEx(interp, name, version, exact, NULL); } const char * Tcl_PkgPresentEx( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ const char *name, /* Name of desired package. */ const char *version, /* Version string for desired version; NULL * means use the latest version available. */ int exact, /* Non-zero means that only the particular * version given is acceptable. Zero means use * the latest compatible version. */ void *clientDataPtr) /* Used to return the client data for this * package. If it is NULL then the client data * is not returned. This is unchanged if this * call fails for any reason. */ { Interp *iPtr = (Interp *) interp; Tcl_HashEntry *hPtr; Package *pkgPtr; hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name); if (hPtr) { pkgPtr = Tcl_GetHashValue(hPtr); if (pkgPtr->version != NULL) { /* * At this point we know that the package is present. Make sure * that the provided version meets the current requirement by * calling Tcl_PkgRequireEx() to check for us. */ const char *foundVersion = Tcl_PkgRequireEx(interp, name, version, exact, clientDataPtr); if (foundVersion == NULL) { Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "PACKAGE", name, NULL); } return foundVersion; } } if (version != NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "package %s %s is not present", name, version)); } else { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "package %s is not present", name)); } Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "PACKAGE", name, NULL); return NULL; } /* *---------------------------------------------------------------------- * * Tcl_PackageObjCmd -- * * This function is invoked to process the "package" Tcl command. See the * user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ /* ARGSUSED */ int Tcl_PackageObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { static const char *const pkgOptions[] = { "forget", "ifneeded", "names", "prefer", "present", "provide", "require", "unknown", "vcompare", "versions", "vsatisfies", NULL }; enum pkgOptions { PKG_FORGET, PKG_IFNEEDED, PKG_NAMES, PKG_PREFER, PKG_PRESENT, PKG_PROVIDE, PKG_REQUIRE, PKG_UNKNOWN, PKG_VCOMPARE, PKG_VERSIONS, PKG_VSATISFIES }; Interp *iPtr = (Interp *) interp; int optionIndex, exact, i, satisfies; PkgAvail *availPtr, *prevPtr; Package *pkgPtr; Tcl_HashEntry *hPtr; Tcl_HashSearch search; Tcl_HashTable *tablePtr; const char *version; const char *argv2, *argv3, *argv4; char *iva = NULL, *ivb = NULL; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], pkgOptions, "option", 0, &optionIndex) != TCL_OK) { return TCL_ERROR; } switch ((enum pkgOptions) optionIndex) { case PKG_FORGET: { const char *keyString; for (i = 2; i < objc; i++) { keyString = TclGetString(objv[i]); hPtr = Tcl_FindHashEntry(&iPtr->packageTable, keyString); if (hPtr == NULL) { continue; } pkgPtr = Tcl_GetHashValue(hPtr); Tcl_DeleteHashEntry(hPtr); if (pkgPtr->version != NULL) { ckfree(pkgPtr->version); } while (pkgPtr->availPtr != NULL) { availPtr = pkgPtr->availPtr; pkgPtr->availPtr = availPtr->nextPtr; Tcl_EventuallyFree(availPtr->version, TCL_DYNAMIC); Tcl_EventuallyFree(availPtr->script, TCL_DYNAMIC); ckfree(availPtr); } ckfree(pkgPtr); } break; } case PKG_IFNEEDED: { int length, res; char *argv3i, *avi; if ((objc != 4) && (objc != 5)) { Tcl_WrongNumArgs(interp, 2, objv, "package version ?script?"); return TCL_ERROR; } argv3 = TclGetString(objv[3]); if (CheckVersionAndConvert(interp, argv3, &argv3i, NULL) != TCL_OK) { return TCL_ERROR; } argv2 = TclGetString(objv[2]); if (objc == 4) { hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2); if (hPtr == NULL) { ckfree(argv3i); return TCL_OK; } pkgPtr = Tcl_GetHashValue(hPtr); } else { pkgPtr = FindPackage(interp, argv2); } argv3 = Tcl_GetStringFromObj(objv[3], &length); for (availPtr = pkgPtr->availPtr, prevPtr = NULL; availPtr != NULL; prevPtr = availPtr, availPtr = availPtr->nextPtr) { if (CheckVersionAndConvert(interp, availPtr->version, &avi, NULL) != TCL_OK) { ckfree(argv3i); return TCL_ERROR; } res = CompareVersions(avi, argv3i, NULL); ckfree(avi); if (res == 0){ if (objc == 4) { ckfree(argv3i); Tcl_SetObjResult(interp, Tcl_NewStringObj(availPtr->script, -1)); return TCL_OK; } Tcl_EventuallyFree(availPtr->script, TCL_DYNAMIC); break; } } ckfree(argv3i); if (objc == 4) { return TCL_OK; } if (availPtr == NULL) { availPtr = ckalloc(sizeof(PkgAvail)); DupBlock(availPtr->version, argv3, (unsigned) length + 1); if (prevPtr == NULL) { availPtr->nextPtr = pkgPtr->availPtr; pkgPtr->availPtr = availPtr; } else { availPtr->nextPtr = prevPtr->nextPtr; prevPtr->nextPtr = availPtr; } } argv4 = Tcl_GetStringFromObj(objv[4], &length); DupBlock(availPtr->script, argv4, (unsigned) length + 1); break; } case PKG_NAMES: if (objc != 2) { Tcl_WrongNumArgs(interp, 2, objv, NULL); return TCL_ERROR; } else { Tcl_Obj *resultObj; resultObj = Tcl_NewObj(); tablePtr = &iPtr->packageTable; for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { pkgPtr = Tcl_GetHashValue(hPtr); if ((pkgPtr->version != NULL) || (pkgPtr->availPtr != NULL)) { Tcl_ListObjAppendElement(NULL,resultObj, Tcl_NewStringObj( Tcl_GetHashKey(tablePtr, hPtr), -1)); } } Tcl_SetObjResult(interp, resultObj); } break; case PKG_PRESENT: { const char *name; if (objc < 3) { goto require; } argv2 = TclGetString(objv[2]); if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) { if (objc != 5) { goto requireSyntax; } exact = 1; name = TclGetString(objv[3]); } else { exact = 0; name = argv2; } hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name); if (hPtr != NULL) { pkgPtr = Tcl_GetHashValue(hPtr); if (pkgPtr->version != NULL) { goto require; } } version = NULL; if (exact) { version = TclGetString(objv[4]); if (CheckVersionAndConvert(interp, version, NULL, NULL) != TCL_OK) { return TCL_ERROR; } } else { if (CheckAllRequirements(interp, objc-3, objv+3) != TCL_OK) { return TCL_ERROR; } if ((objc > 3) && (CheckVersionAndConvert(interp, TclGetString(objv[3]), NULL, NULL) == TCL_OK)) { version = TclGetString(objv[3]); } } Tcl_PkgPresentEx(interp, name, version, exact, NULL); return TCL_ERROR; break; } case PKG_PROVIDE: if ((objc != 3) && (objc != 4)) { Tcl_WrongNumArgs(interp, 2, objv, "package ?version?"); return TCL_ERROR; } argv2 = TclGetString(objv[2]); if (objc == 3) { hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2); if (hPtr != NULL) { pkgPtr = Tcl_GetHashValue(hPtr); if (pkgPtr->version != NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj(pkgPtr->version, -1)); } } return TCL_OK; } argv3 = TclGetString(objv[3]); if (CheckVersionAndConvert(interp, argv3, NULL, NULL) != TCL_OK) { return TCL_ERROR; } return Tcl_PkgProvideEx(interp, argv2, argv3, NULL); case PKG_REQUIRE: require: if (objc < 3) { requireSyntax: Tcl_WrongNumArgs(interp, 2, objv, "?-exact? package ?requirement ...?"); return TCL_ERROR; } version = NULL; argv2 = TclGetString(objv[2]); if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) { Tcl_Obj *ov; int res; if (objc != 5) { goto requireSyntax; } version = TclGetString(objv[4]); if (CheckVersionAndConvert(interp, version, NULL, NULL) != TCL_OK) { return TCL_ERROR; } /* * Create a new-style requirement for the exact version. */ ov = Tcl_NewStringObj(version, -1); Tcl_AppendStringsToObj(ov, "-", version, NULL); version = NULL; argv3 = TclGetString(objv[3]); Tcl_IncrRefCount(ov); res = Tcl_PkgRequireProc(interp, argv3, 1, &ov, NULL); TclDecrRefCount(ov); return res; } else { if (CheckAllRequirements(interp, objc-3, objv+3) != TCL_OK) { return TCL_ERROR; } return Tcl_PkgRequireProc(interp, argv2, objc-3, objv+3, NULL); } break; case PKG_UNKNOWN: { int length; if (objc == 2) { if (iPtr->packageUnknown != NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj(iPtr->packageUnknown, -1)); } } else if (objc == 3) { if (iPtr->packageUnknown != NULL) { ckfree(iPtr->packageUnknown); } argv2 = Tcl_GetStringFromObj(objv[2], &length); if (argv2[0] == 0) { iPtr->packageUnknown = NULL; } else { DupBlock(iPtr->packageUnknown, argv2, (unsigned) length+1); } } else { Tcl_WrongNumArgs(interp, 2, objv, "?command?"); return TCL_ERROR; } break; } case PKG_PREFER: { static const char *const pkgPreferOptions[] = { "latest", "stable", NULL }; /* * See tclInt.h for the enum, just before Interp. */ if (objc > 3) { Tcl_WrongNumArgs(interp, 2, objv, "?latest|stable?"); return TCL_ERROR; } else if (objc == 3) { /* * Seting the value. */ int newPref; if (Tcl_GetIndexFromObj(interp, objv[2], pkgPreferOptions, "preference", 0, &newPref) != TCL_OK) { return TCL_ERROR; } if (newPref < iPtr->packagePrefer) { iPtr->packagePrefer = newPref; } } /* * Always return current value. */ Tcl_SetObjResult(interp, Tcl_NewStringObj(pkgPreferOptions[iPtr->packagePrefer], -1)); break; } case PKG_VCOMPARE: if (objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, "version1 version2"); return TCL_ERROR; } argv3 = TclGetString(objv[3]); argv2 = TclGetString(objv[2]); if (CheckVersionAndConvert(interp, argv2, &iva, NULL) != TCL_OK || CheckVersionAndConvert(interp, argv3, &ivb, NULL) != TCL_OK) { if (iva != NULL) { ckfree(iva); } /* * ivb cannot be set in this branch. */ return TCL_ERROR; } /* * Comparison is done on the internal representation. */ Tcl_SetObjResult(interp, Tcl_NewIntObj(CompareVersions(iva, ivb, NULL))); ckfree(iva); ckfree(ivb); break; case PKG_VERSIONS: if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "package"); return TCL_ERROR; } else { Tcl_Obj *resultObj = Tcl_NewObj(); argv2 = TclGetString(objv[2]); hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2); if (hPtr != NULL) { pkgPtr = Tcl_GetHashValue(hPtr); for (availPtr = pkgPtr->availPtr; availPtr != NULL; availPtr = availPtr->nextPtr) { Tcl_ListObjAppendElement(NULL, resultObj, Tcl_NewStringObj(availPtr->version, -1)); } } Tcl_SetObjResult(interp, resultObj); } break; case PKG_VSATISFIES: { char *argv2i = NULL; if (objc < 4) { Tcl_WrongNumArgs(interp, 2, objv, "version ?requirement ...?"); return TCL_ERROR; } argv2 = TclGetString(objv[2]); if (CheckVersionAndConvert(interp, argv2, &argv2i, NULL) != TCL_OK) { return TCL_ERROR; } else if (CheckAllRequirements(interp, objc-3, objv+3) != TCL_OK) { ckfree(argv2i); return TCL_ERROR; } satisfies = SomeRequirementSatisfied(argv2i, objc-3, objv+3); ckfree(argv2i); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(satisfies)); break; } default: Tcl_Panic("Tcl_PackageObjCmd: bad option index to pkgOptions"); } return TCL_OK; } /* *---------------------------------------------------------------------- * * FindPackage -- * * This function finds the Package record for a particular package in a * particular interpreter, creating a record if one doesn't already * exist. * * Results: * The return value is a pointer to the Package record for the package. * * Side effects: * A new Package record may be created. * *---------------------------------------------------------------------- */ static Package * FindPackage( Tcl_Interp *interp, /* Interpreter to use for package lookup. */ const char *name) /* Name of package to fine. */ { Interp *iPtr = (Interp *) interp; Tcl_HashEntry *hPtr; int isNew; Package *pkgPtr; hPtr = Tcl_CreateHashEntry(&iPtr->packageTable, name, &isNew); if (isNew) { pkgPtr = ckalloc(sizeof(Package)); pkgPtr->version = NULL; pkgPtr->availPtr = NULL; pkgPtr->clientData = NULL; Tcl_SetHashValue(hPtr, pkgPtr); } else { pkgPtr = Tcl_GetHashValue(hPtr); } return pkgPtr; } /* *---------------------------------------------------------------------- * * TclFreePackageInfo -- * * This function is called during interpreter deletion to free all of the * package-related information for the interpreter. * * Results: * None. * * Side effects: * Memory is freed. * *---------------------------------------------------------------------- */ void TclFreePackageInfo( Interp *iPtr) /* Interpereter that is being deleted. */ { Package *pkgPtr; Tcl_HashSearch search; Tcl_HashEntry *hPtr; PkgAvail *availPtr; for (hPtr = Tcl_FirstHashEntry(&iPtr->packageTable, &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { pkgPtr = Tcl_GetHashValue(hPtr); if (pkgPtr->version != NULL) { ckfree(pkgPtr->version); } while (pkgPtr->availPtr != NULL) { availPtr = pkgPtr->availPtr; pkgPtr->availPtr = availPtr->nextPtr; Tcl_EventuallyFree(availPtr->version, TCL_DYNAMIC); Tcl_EventuallyFree(availPtr->script, TCL_DYNAMIC); ckfree(availPtr); } ckfree(pkgPtr); } Tcl_DeleteHashTable(&iPtr->packageTable); if (iPtr->packageUnknown != NULL) { ckfree(iPtr->packageUnknown); } } /* *---------------------------------------------------------------------- * * CheckVersionAndConvert -- * * This function checks to see whether a version number has valid syntax. * It also generates a semi-internal representation (string rep of a list * of numbers). * * Results: * If string is a properly formed version number the TCL_OK is returned. * Otherwise TCL_ERROR is returned and an error message is left in the * interp's result. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int CheckVersionAndConvert( Tcl_Interp *interp, /* Used for error reporting. */ const char *string, /* Supposedly a version number, which is * groups of decimal digits separated by * dots. */ char **internal, /* Internal normalized representation */ int *stable) /* Flag: Version is (un)stable. */ { const char *p = string; char prevChar; int hasunstable = 0; /* * 4* assuming that each char is a separator (a,b become ' -x '). * 4+ to have spce for an additional -2 at the end */ char *ibuf = ckalloc(4 + 4*strlen(string)); char *ip = ibuf; /* * Basic rules * (1) First character has to be a digit. * (2) All other characters have to be a digit or '.' * (3) Two '.'s may not follow each other. * * TIP 268, Modified rules * (1) s.a. * (2) All other characters have to be a digit, 'a', 'b', or '.' * (3) s.a. * (4) Only one of 'a' or 'b' may occur. * (5) Neither 'a' /******************/ /* User data for direct block debugging iterator callback */ typedef struct { FILE *stream; /* Stream for output */ int indent; /* Indention amount */ int fwidth; /* Field width mount */ haddr_t dblock_addr; /* Direct block's address */ hsize_t dblock_size; /* Direct block's size */ uint8_t *marker; /* 'Marker' array for free space */ size_t sect_count; /* Number of free space sections in block */ size_t amount_free; /* Amount of free space in block */ } H5HF_debug_iter_ud1_t; /* User data for free space section iterator callback */ typedef struct { H5FS_t *fspace; /* Free space manager */ FILE *stream; /* Stream for output */ int indent; /* Indention amount */ int fwidth; /* Field width mount */ } H5HF_debug_iter_ud2_t; /********************/ /* Package Typedefs */ /********************/ /********************/ /* Local Prototypes */ /********************/ static herr_t H5HF_dtable_debug(H5HF_dtable_t *dtable, FILE *stream, int indent, int fwidth); /*********************/ /* Package Variables */ /*********************/ /*****************************/ /* Library Private Variables */ /*****************************/ /*******************/ /* Local Variables */ /*******************/ /*------------------------------------------------------------------------- * Function: H5HF_dtable_debug * * Purpose: Prints debugging info about a doubling table * * Return: Non-negative on success/Negative on failure * * Programmer: Quincey Koziol * koziol@ncsa.uiuc.edu * Feb 28 2006 * *------------------------------------------------------------------------- */ static herr_t H5HF_dtable_debug(H5HF_dtable_t *dtable, FILE *stream, int indent, int fwidth) { FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5HF_dtable_debug) /* * Check arguments. */ HDassert(dtable); HDassert(stream); HDassert(indent >= 0); HDassert(fwidth >= 0); /* * Print the values. */ /* Creation parameter values */ HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Doubling table width:", dtable->cparam.width); HDfprintf(stream, "%*s%-*s %Zu\n", indent, "", fwidth, "Starting block size:", dtable->cparam.start_block_size); HDfprintf(stream, "%*s%-*s %Zu\n", indent, "", fwidth, "Max. direct block size:", dtable->cparam.max_direct_size); HDfprintf(stream, "%*s%-*s %u (bits)\n", indent, "", fwidth, "Max. index size:", dtable->cparam.max_index); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Starting # of rows in root indirect block:", dtable->cparam.start_root_rows); /* Run-time varying parameter values */ HDfprintf(stream, "%*s%-*s %a\n", indent, "", fwidth, "Table's root address:", dtable->table_addr); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Current # of rows in root indirect block:", dtable->curr_root_rows); /* Computed values */ HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Max. # of rows in root indirect block:", dtable->max_root_rows); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Max. # of direct rows in any indirect block:", dtable->max_direct_rows); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "# of bits for IDs in first row:", dtable->first_row_bits); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "# of IDs in first row:", dtable->num_id_first_row); FUNC_LEAVE_NOAPI(SUCCEED) } /* end H5HF_dtable_debug() */ /*------------------------------------------------------------------------- * Function: H5HF_hdr_debug * * Purpose: Prints debugging info about a fractal heap header. * * Return: Non-negative on success/Negative on failure * * Programmer: Quincey Koziol * koziol@ncsa.uiuc.edu * Feb 24 2006 * *------------------------------------------------------------------------- */ herr_t H5HF_hdr_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr, FILE *stream, int indent, int fwidth) { H5HF_hdr_t *hdr = NULL; /* Fractal heap header info */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(H5HF_hdr_debug, FAIL) /* * Check arguments. */ HDassert(f); HDassert(H5F_addr_defined(addr)); HDassert(stream); HDassert(indent >= 0); HDassert(fwidth >= 0); /* * Load the fractal heap header. */ if(NULL == (hdr = (H5HF_hdr_t *)H5AC_protect(f, dxpl_id, H5AC_FHEAP_HDR, addr, NULL, NULL, H5AC_READ))) HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, FAIL, "unable to load fractal heap header") /* Print opening message */ HDfprintf(stream, "%*sFractal Heap Header...\n", indent, ""); /* * Print the values. */ HDfprintf(stream, "%*s%-*s %s\n", indent, "", fwidth, "Heap is:", hdr->man_dtable.curr_root_rows > 0 ? "Indirect" : "Direct"); HDfprintf(stream, "%*s%-*s %t\n", indent, "", fwidth, "Objects stored in 'debugging' format:", hdr->debug_objs); HDfprintf(stream, "%*s%-*s %t\n", indent, "", fwidth, "'Write once' flag:", hdr->write_once); HDfprintf(stream, "%*s%-*s %t\n", indent, "", fwidth, "'Huge' object IDs have wrapped:", hdr->huge_ids_wrapped); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "Free space in managed blocks:", hdr->total_man_free); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "Managed space data block size:", hdr->man_size); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "Total managed space allocated:", hdr->man_alloc_size); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "Offset of managed space iterator:", hdr->man_iter_off); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "Number of managed objects in heap:", hdr->man_nobjs); HDfprintf(stream, "%*s%-*s %a\n", indent, "", fwidth, "Address of free space manager for managed blocks:", hdr->fs_addr); HDfprintf(stream, "%*s%-*s %lu\n", indent, "", fwidth, "Max. size of managed object:", (unsigned long)hdr->max_man_size); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "'Huge' object space used:", hdr->huge_size); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "Number of 'huge' objects in heap:", hdr->huge_nobjs); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "ID of next 'huge' object:", hdr->huge_next_id); HDfprintf(stream, "%*s%-*s %a\n", indent, "", fwidth, "Address of v2 B-tree for 'huge' objects:", hdr->huge_bt2_addr); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "'Tiny' object space used:", hdr->tiny_size); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "Number of 'tiny' objects in heap:", hdr->tiny_nobjs); HDfprintf(stream, "%*sManaged Objects Doubling-Table Info...\n", indent, ""); H5HF_dtable_debug(&hdr->man_dtable, stream, indent + 3, MAX(0, fwidth - 3)); /* Print information about I/O filters */ if(hdr->filter_len > 0) { HDfprintf(stream, "%*sI/O filter Info...\n", indent, ""); if(hdr->man_dtable.curr_root_rows == 0) { HDfprintf(stream, "%*s%-*s %Zu\n", indent + 3, "", MAX(0, fwidth - 3), "Compressed size of root direct block:", hdr->pline_root_direct_size); HDfprintf(stream, "%*s%-*s %x\n", indent + 3, "", MAX(0, fwidth - 3), "Filter mask for root direct block:", hdr->pline_root_direct_filter_mask); } /* end if */ H5O_debug_id(H5O_PLINE_ID, f, dxpl_id, &(hdr->pline), stream, indent + 3, MAX(0, fwidth - 3)); } /* end if */ done: if(hdr && H5AC_unprotect(f, dxpl_id, H5AC_FHEAP_HDR, addr, hdr, H5AC__NO_FLAGS_SET) < 0) HDONE_ERROR(H5E_HEAP, H5E_PROTECT, FAIL, "unable to release fractal heap header") FUNC_LEAVE_NOAPI(ret_value) } /* end H5HF_hdr_debug() */ /*------------------------------------------------------------------------- * Function: H5HF_dblock_debug_cb * * Purpose: Detect free space within a direct block * * Return: Non-negative on success/Negative on failure * * Programmer: Quincey Koziol * koziol@ncsa.uiuc.edu * May 13 2006 * *------------------------------------------------------------------------- */ static herr_t H5HF_dblock_debug_cb(const H5FS_section_info_t *_sect, void *_udata) { const H5HF_free_section_t *sect = (const H5HF_free_section_t *)_sect; /* Section to dump info */ H5HF_debug_iter_ud1_t *udata = (H5HF_debug_iter_ud1_t *)_udata; /* User data for callbacks */ haddr_t sect_start, sect_end; /* Section's beginning and ending offsets */ haddr_t dblock_start, dblock_end; /* Direct block's beginning and ending offsets */ FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5HF_dblock_debug_cb) /* * Check arguments. */ HDassert(sect); HDassert(udata); /* Set up some local variables, for convenience */ sect_start = sect->sect_info.addr; sect_end = (sect->sect_info.addr + sect->sect_info.size) - 1; HDassert(sect_end >= sect_start); dblock_start = udata->dblock_addr; dblock_end = (udata->dblock_addr + udata->dblock_size) - 1; HDassert(dblock_end >= dblock_start); /* Check for overlap between free space section & direct block */ if((sect_start <= dblock_end && sect_end >=dblock_start) || /* section within or overlaps w/beginning of direct block*/ (sect_start <= dblock_end && sect_end >=dblock_end)) { /* section overlaps w/end of direct block */ char temp_str[32]; /* Temporary string for formatting */ size_t start, end; /* Start & end of the overlapping area */ size_t len; /* Length of the overlapping area */ size_t overlap; /* Track any overlaps */ unsigned u; /* Local index variable */ /* Calculate the starting & ending */ if(sect_start < dblock_start) start = 0; else H5_ASSIGN_OVERFLOW(/* To: */ start, /* From: */ (sect_start - dblock_start), /* From: */ hsize_t, /* To: */ size_t) if(sect_end > dblock_end) H5_ASSIGN_OVERFLOW(/* To: */ end, /* From: */ udata->dblock_size, /* From: */ hsize_t, /* To: */ size_t) else H5_ASSIGN_OVERFLOW(/* To: */ end, /* From: */ ((sect_end - dblock_start) + 1), /* From: */ hsize_t, /* To: */ size_t) /* Calculate the length */ len = end - start; sprintf(temp_str, "Section #%u:", (unsigned)udata->sect_count); HDfprintf(udata->stream, "%*s%-*s %8Zu, %8Zu\n", udata->indent + 3, "", MAX(0, udata->fwidth - 9), temp_str, start, len); udata->sect_count++; /* Mark this node's free space & check for overlaps w/other sections */ overlap = 0; for(u = start; u < end; u++) { if(udata->marker[u]) overlap++; udata->marker[u] = 1; } /* end for */ /* Flag overlaps */ if (overlap) fprintf(udata->stream, "***THAT FREE BLOCK OVERLAPPED A PREVIOUS ONE!\n"); else udata->amount_free += len; } /* end if */ FUNC_LEAVE_NOAPI(SUCCEED) } /* end H5HF_dblock_debug_cb() */ /*------------------------------------------------------------------------- * Function: H5HF_dblock_debug * * Purpose: Prints debugging info about a fractal heap direct block. * * Return: Non-negative on success/Negative on failure * * Programmer: Quincey Koziol * koziol@ncsa.uiuc.edu * Feb 28 2006 * *------------------------------------------------------------------------- */ herr_t H5HF_dblock_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr, FILE *stream, int indent, int fwidth, haddr_t hdr_addr, size_t block_size) { H5HF_hdr_t *hdr = NULL; /* Fractal heap header info */ H5HF_direct_t *dblock = NULL; /* Fractal heap direct block info */ size_t blk_prefix_size; /* Size of prefix for block */ size_t amount_free; /* Amount of free space in block */ uint8_t *marker = NULL; /* Track free space for block */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(H5HF_dblock_debug, FAIL) /* * Check arguments. */ HDassert(f); HDassert(H5F_addr_defined(addr)); HDassert(stream); HDassert(indent >= 0); HDassert(fwidth >= 0); HDassert(H5F_addr_defined(hdr_addr)); HDassert(block_size > 0); /* * Load the fractal heap header. */ if(NULL == (hdr = (H5HF_hdr_t *)H5AC_protect(f, dxpl_id, H5AC_FHEAP_HDR, hdr_addr, NULL, NULL, H5AC_READ))) HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, FAIL, "unable to load fractal heap header") /* * Load the heap direct block */ if(NULL == (dblock = H5HF_man_dblock_protect(hdr, dxpl_id, addr, block_size, NULL, 0, H5AC_READ))) HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, FAIL, "unable to load fractal heap direct block") /* Print opening message */ HDfprintf(stream, "%*sFractal Heap Direct Block...\n", indent, ""); /* * Print the values. */ HDfprintf(stream, "%*s%-*s %a\n", indent, "", fwidth, "Address of fractal heap that owns this block:", hdr->heap_addr); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "Offset of direct block in heap:", dblock->block_off); blk_prefix_size = H5HF_MAN_ABS_DIRECT_OVERHEAD(hdr); HDfprintf(stream, "%*s%-*s %Zu\n", indent, "", fwidth, "Size of block header:", blk_prefix_size); /* Allocate space for the free space markers */ if(NULL == (marker = (uint8_t *)H5MM_calloc(dblock->size))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") /* Initialize the free space information for the heap */ if(H5HF_space_start(hdr, dxpl_id, FALSE) < 0) HGOTO_ERROR(H5E_HEAP, H5E_CANTINIT, FAIL, "can't initialize heap free space") /* If there is a free space manager for the heap, check for sections that overlap this block */ if(hdr->fspace) { H5HF_debug_iter_ud1_t udata; /* User data for callbacks */ /* Prepare user data for section iteration callback */ udata.stream = stream; udata.indent = indent; udata.fwidth = fwidth; udata.dblock_addr = dblock->block_off; udata.dblock_size = block_size; udata.marker = marker; udata.sect_count = 0; udata.amount_free = 0; /* Print header */ HDfprintf(stream, "%*sFree Blocks (offset, size):\n", indent, ""); /* Iterate over the free space sections, to detect overlaps with this block */ if(H5FS_sect_iterate(f, dxpl_id, hdr->fspace, H5HF_dblock_debug_cb, &udata) < 0) HGOTO_ERROR(H5E_HEAP, H5E_BADITER, FAIL, "can't iterate over heap's free space") /* Close the free space information */ if(H5HF_space_close(hdr, dxpl_id) < 0) HGOTO_ERROR(H5E_HEAP, H5E_CANTRELEASE, FAIL, "can't release free space info") /* Keep the amount of space free */ amount_free = udata.amount_free; /* Check for no free space */ if(amount_free == 0) HDfprintf(stream, "%*s<none>\n", indent + 3, ""); } /* end if */ else amount_free = 0; HDfprintf(stream, "%*s%-*s %.2f%%\n", indent, "", fwidth, "Percent of available space for data used:", (100.0 * (double)((dblock->size - blk_prefix_size) - amount_free) / (double)(dblock->size - blk_prefix_size))); /* * Print the data in a VMS-style octal dump. */ H5_buffer_dump(stream, indent, dblock->blk, marker, (size_t)0, dblock->size); done: if(dblock && H5AC_unprotect(f, dxpl_id, H5AC_FHEAP_DBLOCK, addr, dblock, H5AC__NO_FLAGS_SET) < 0) HDONE_ERROR(H5E_HEAP, H5E_PROTECT, FAIL, "unable to release fractal heap direct block") if(hdr && H5AC_unprotect(f, dxpl_id, H5AC_FHEAP_HDR, hdr_addr, hdr, H5AC__NO_FLAGS_SET) < 0) HDONE_ERROR(H5E_HEAP, H5E_PROTECT, FAIL, "unable to release fractal heap header") H5MM_xfree(marker); FUNC_LEAVE_NOAPI(ret_value) } /* end H5HF_dblock_debug() */ /*------------------------------------------------------------------------- * Function: H5HF_iblock_debug * * Purpose: Prints debugging info about a fractal heap indirect block. * * Return: Non-negative on success/Negative on failure * * Programmer: Quincey Koziol * koziol@ncsa.uiuc.edu * Mar 7 2006 * *------------------------------------------------------------------------- */ herr_t H5HF_iblock_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr, FILE *stream, int indent, int fwidth, haddr_t hdr_addr, unsigned nrows) { H5HF_hdr_t *hdr = NULL; /* Fractal heap header info */ H5HF_indirect_t *iblock = NULL; /* Fractal heap direct block info */ hbool_t did_protect; /* Whether we protected the indirect block or not */ char temp_str[64]; /* Temporary string, for formatting */ size_t u, v; /* Local index variable */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(H5HF_iblock_debug, FAIL) /* * Check arguments. */ HDassert(f); HDassert(H5F_addr_defined(addr)); HDassert(stream); HDassert(indent >= 0); HDassert(fwidth >= 0); HDassert(H5F_addr_defined(hdr_addr)); HDassert(nrows > 0); /* * Load the fractal heap header. */ if(NULL == (hdr = (H5HF_hdr_t *)H5AC_protect(f, dxpl_id, H5AC_FHEAP_HDR, hdr_addr, NULL, NULL, H5AC_READ))) HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, FAIL, "unable to load fractal heap header") /* * Load the heap indirect block */ if(NULL == (iblock = H5HF_man_iblock_protect(hdr, dxpl_id, addr, nrows, NULL, 0, FALSE, H5AC_READ, &did_protect))) HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, FAIL, "unable to load fractal heap indirect block") /* Print opening message */ HDfprintf(stream, "%*sFractal Heap Indirect Block...\n", indent, ""); /* * Print the values. */ HDfprintf(stream, "%*s%-*s %a\n", indent, "", fwidth, "Address of fractal heap that owns this block:", hdr->heap_addr); HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, "Offset of indirect block in heap:", iblock->block_off); HDfprintf(stream, "%*s%-*s %Zu\n", indent, "", fwidth, "Size of indirect block:", iblock->size); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Current # of rows:", iblock->nrows); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Max. # of rows:", iblock->max_rows); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Max direct block rows:", hdr->man_dtable.max_direct_rows); /* Print the entry tables */ if(hdr->filter_len > 0) HDfprintf(stream, "%*sDirect Block Entries: (address/compressed size/filter mask)\n", indent, ""); else HDfprintf(stream, "%*sDirect Block Entries: (address)\n", indent, ""); for(u = 0; u < hdr->man_dtable.max_direct_rows && u < iblock->nrows; u++) { sprintf(temp_str, "Row #%u: (block size: %lu)", (unsigned)u, (unsigned long)hdr->man_dtable.row_block_size[u]); HDfprintf(stream, "%*s%-*s\n", indent + 3, "", MAX(0, fwidth - 3), temp_str); for(v = 0; v < hdr->man_dtable.cparam.width; v++) { size_t off = (u * hdr->man_dtable.cparam.width) + v; sprintf(temp_str, "Col #%u:", (unsigned)v); if(hdr->filter_len > 0) HDfprintf(stream, "%*s%-*s %9a/%6Zu/%x\n", indent + 6, "", MAX(0, fwidth - 6), temp_str, iblock->ents[off].addr, iblock->filt_ents[off].size, iblock->filt_ents[off].filter_mask); else HDfprintf(stream, "%*s%-*s %9a\n", indent + 6, "", MAX(0, fwidth - 6), temp_str, iblock->ents[off].addr); } /* end for */ } /* end for */ HDfprintf(stream, "%*sIndirect Block Entries:\n", indent, ""); if(iblock->nrows > hdr->man_dtable.max_direct_rows) { unsigned first_row_bits; /* Number of bits used bit addresses in first row */ unsigned num_indirect_rows; /* Number of rows of blocks in each indirect block */ first_row_bits = H5V_log2_of2((uint32_t)hdr->man_dtable.cparam.start_block_size) + H5V_log2_of2(hdr->man_dtable.cparam.width); for(u = hdr->man_dtable.max_direct_rows; u < iblock->nrows; u++) { num_indirect_rows = (H5V_log2_gen(hdr->man_dtable.row_block_size[u]) - first_row_bits) + 1; sprintf(temp_str, "Row #%u: (# of rows: %u)", (unsigned)u, num_indirect_rows); HDfprintf(stream, "%*s%-*s\n", indent + 3, "", MAX(0, fwidth - 3), temp_str); for(v = 0; v < hdr->man_dtable.cparam.width; v++) { size_t off = (u * hdr->man_dtable.cparam.width) + v; sprintf(temp_str, "Col #%u:", (unsigned)v); HDfprintf(stream, "%*s%-*s %9a\n", indent + 6, "", MAX(0, fwidth - 6), temp_str, iblock->ents[off].addr); } /* end for */ } /* end for */ } /* end if */ else HDfprintf(stream, "%*s%-*s\n", indent + 3, "", MAX(0, fwidth - 3), "<none>"); done: if(iblock && H5HF_man_iblock_unprotect(iblock, dxpl_id, H5AC__NO_FLAGS_SET, did_protect) < 0) HDONE_ERROR(H5E_HEAP, H5E_PROTECT, FAIL, "unable to release fractal heap direct block") if(hdr && H5AC_unprotect(f, dxpl_id, H5AC_FHEAP_HDR, hdr_addr, hdr, H5AC__NO_FLAGS_SET) < 0) HDONE_ERROR(H5E_HEAP, H5E_PROTECT, FAIL, "unable to release fractal heap header") FUNC_LEAVE_NOAPI(ret_value) } /* end H5HF_iblock_debug() */ /*------------------------------------------------------------------------- * Function: H5HF_sects_debug_cb * * Purpose: Prints debugging info about a free space section for a fractal heap. * * Return: Non-negative on success/Negative on failure * * Programmer: Quincey Koziol * koziol@ncsa.uiuc.edu * May 13 2006 * *------------------------------------------------------------------------- */ static herr_t H5HF_sects_debug_cb(const H5FS_section_info_t *_sect, void *_udata) { const H5HF_free_section_t *sect = (const H5HF_free_section_t *)_sect; /* Section to dump info */ H5HF_debug_iter_ud2_t *udata = (H5HF_debug_iter_ud2_t *)_udata; /* User data for callbacks */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI_NOINIT(H5HF_sects_debug_cb) /* * Check arguments. */ HDassert(sect); HDassert(udata); /* Print generic section information */ HDfprintf(udata->stream, "%*s%-*s %s\n", udata->indent, "", udata->fwidth, "Section type:", (sect->sect_info.type == H5HF_FSPACE_SECT_SINGLE ? "single" : (sect->sect_info.type == H5HF_FSPACE_SECT_FIRST_ROW ? "first row" : (sect->sect_info.type == H5HF_FSPACE_SECT_NORMAL_ROW ? "normal row" : "unknown")))); HDfprintf(udata->stream, "%*s%-*s %a\n", udata->indent, "", udata->fwidth, "Section address:", sect->sect_info.addr); HDfprintf(udata->stream, "%*s%-*s %Hu\n", udata->indent, "", udata->fwidth, "Section size:", sect->sect_info.size); #ifdef QAK HDfprintf(udata->stream, "%*s%-*s %s\n", udata->indent, "", udata->fwidth, "Section state:", (sect->sect_info.state == H5FS_SECT_LIVE ? "live" : "serialized")); #endif /* QAK */ /* Dump section-specific debugging information */ if(H5FS_sect_debug(udata->fspace, _sect, udata->stream, udata->indent + 3, MAX(0, udata->fwidth - 3)) < 0) HGOTO_ERROR(H5E_HEAP, H5E_BADITER, FAIL, "can't dump section's debugging info") done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5HF_sects_debug_cb() */ /*------------------------------------------------------------------------- * Function: H5HF_sects_debug * * Purpose: Prints debugging info about free space sections for a fractal heap. * * Return: Non-negative on success/Negative on failure * * Programmer: Quincey Koziol * koziol@ncsa.uiuc.edu * May 9 2006 * *------------------------------------------------------------------------- */ herr_t H5HF_sects_debug(H5F_t *f, hid_t dxpl_id, haddr_t fh_addr, FILE *stream, int indent, int fwidth) { H5HF_hdr_t *hdr = NULL; /* Fractal heap header info */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(H5HF_sects_debug, FAIL) /* * Check arguments. */ HDassert(f); HDassert(H5F_addr_defined(fh_addr)); HDassert(stream); HDassert(indent >= 0); HDassert(fwidth >= 0); /* * Load the fractal heap header. */ if(NULL == (hdr = (H5HF_hdr_t *)H5AC_protect(f, dxpl_id, H5AC_FHEAP_HDR, fh_addr, NULL, NULL, H5AC_READ))) HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, FAIL, "unable to load fractal heap header") /* Initialize the free space information for the heap */ if(H5HF_space_start(hdr, dxpl_id, FALSE) < 0) HGOTO_ERROR(H5E_HEAP, H5E_CANTINIT, FAIL, "can't initialize heap free space") /* If there is a free space manager for the heap, iterate over them */ if(hdr->fspace) { H5HF_debug_iter_ud2_t udata; /* User data for callbacks */ /* Prepare user data for section iteration callback */ udata.fspace = hdr->fspace; udata.stream = stream; udata.indent = indent; udata.fwidth = fwidth; /* Iterate over all the free space sections */ if(H5FS_sect_iterate(f, dxpl_id, hdr->fspace, H5HF_sects_debug_cb, &udata) < 0) HGOTO_ERROR(H5E_HEAP, H5E_BADITER, FAIL, "can't iterate over heap's free space") /* Close the free space information */ if(H5HF_space_close(hdr, dxpl_id) < 0) HGOTO_ERROR(H5E_HEAP, H5E_CANTRELEASE, FAIL, "can't release free space info") } /* end if */ done: if(hdr && H5AC_unprotect(f, dxpl_id, H5AC_FHEAP_HDR, fh_addr, hdr, H5AC__NO_FLAGS_SET) < 0) HDONE_ERROR(H5E_HEAP, H5E_PROTECT, FAIL, "unable to release fractal heap header") FUNC_LEAVE_NOAPI(ret_value) } /* end H5HF_sects_debug() */