/* * tclMacOSXBundle.c -- * * This file implements functions that inspect CFBundle structures on * MacOS X. * * Copyright 2001-2009, Apple Inc. * Copyright (c) 2003-2009 Daniel A. Steffen * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclPort.h" #ifdef HAVE_COREFOUNDATION #include #ifndef TCL_DYLD_USE_DLFCN /* * Use preferred dlfcn API on 10.4 and later */ # if !defined(NO_DLFCN_H) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 # define TCL_DYLD_USE_DLFCN 1 # else # define TCL_DYLD_USE_DLFCN 0 # endif #endif /* TCL_DYLD_USE_DLFCN */ #ifndef TCL_DYLD_USE_NSMODULE /* * Use deprecated NSModule API only to support 10.3 and earlier: */ # if MAC_OS_X_VERSION_MIN_REQUIRED < 1040 # define TCL_DYLD_USE_NSMODULE 1 # else # define TCL_DYLD_USE_NSMODULE 0 # endif #endif /* TCL_DYLD_USE_NSMODULE */ #if TCL_DYLD_USE_DLFCN #include #if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040 /* * Support for weakly importing dlfcn API. */ extern void * dlsym(void *handle, const char *symbol) WEAK_IMPORT_ATTRIBUTE; extern char * dlerror(void) WEAK_IMPORT_ATTRIBUTE; #endif #endif /* TCL_DYLD_USE_DLFCN */ #if TCL_DYLD_USE_NSMODULE #include #endif #if (TCL_DYLD_USE_DLFCN && MAC_OS_X_VERSION_MIN_REQUIRED < 1040) || \ (MAC_OS_X_VERSION_MIN_REQUIRED < 1050) MODULE_SCOPE long tclMacOSXDarwinRelease; #endif #ifdef TCL_DEBUG_LOAD #define TclLoadDbgMsg(m, ...) \ do { \ fprintf(stderr, "%s:%d: %s(): " m ".\n", \ strrchr(__FILE__, '/')+1, __LINE__, __func__, \ ##__VA_ARGS__); \ } while (0) #else #define TclLoadDbgMsg(m, ...) #endif /* TCL_DEBUG_LOAD */ /* * Forward declaration of functions defined in this file: */ static short OpenResourceMap(CFBundleRef bundleRef); #endif /* HAVE_COREFOUNDATION */ /* *---------------------------------------------------------------------- * * OpenResourceMap -- * * Wrapper that dynamically acquires the address for the function * CFBundleOpenBundleResourceMap before calling it, since it is only * present in full CoreFoundation on Mac OS X and not in CFLite on pure * Darwin. Factored out because it is moderately ugly code. * *---------------------------------------------------------------------- */ #ifdef HAVE_COREFOUNDATION static short OpenResourceMap( CFBundleRef bundleRef) { static int initialized = FALSE; static short (*openresourcemap)(CFBundleRef) = NULL; if (!initialized) { #if TCL_DYLD_USE_DLFCN #if MAC_OS_X_VERSION_MIN_REQUIRED < 1040 if (tclMacOSXDarwinRelease >= 8) #endif { openresourcemap = dlsym(RTLD_NEXT, "CFBundleOpenBundleResourceMap"); #ifdef TCL_DEBUG_LOAD if (!openresourcemap) { const char *errMsg = dlerror(); TclLoadDbgMsg("dlsym() failed: %s", errMsg); } #endif /* TCL_DEBUG_LOAD */ } if (!openresourcemap) #endif /* TCL_DYLD_USE_DLFCN */ { #if TCL_DYLD_USE_NSMODULE if (NSIsSymbolNameDefinedWithHint( "_CFBundleOpenBundleResourceMap", "CoreFoundation")) { NSSymbol nsSymbol = NSLookupAndBindSymbolWithHint( "_CFBundleOpenBundleResourceMap", "CoreFoundation"); if (nsSymbol) { openresourcemap = NSAddressOfSymbol(nsSymbol); } } #endif /* TCL_DYLD_USE_NSMODULE */ } initialized = TRUE; } if (openresourcemap) { return openresourcemap(bundleRef); } return -1; } #endif /* HAVE_COREFOUNDATION */ /* *---------------------------------------------------------------------- * * Tcl_MacOSXOpenBundleResources -- * * Given the bundle name for a shared library, this routine sets * libraryPath to the Resources/Scripts directory in the framework * package. If hasResourceFile is true, it will also open the main * resource file for the bundle. * * Results: * TCL_OK if the bundle could be opened, and the Scripts folder found. * TCL_ERROR otherwise. * * Side effects: * libraryVariableName may be set, and the resource file opened. * *---------------------------------------------------------------------- */ #undef Tcl_MacOSXOpenBundleResources int Tcl_MacOSXOpenBundleResources( Tcl_Interp *interp, const char *bundleName, int hasResourceFile, int maxPathLen, char *libraryPath) { return Tcl_MacOSXOpenVersionedBundleResources(interp, bundleName, NULL, hasResourceFile, maxPathLen, libraryPath); } /* *---------------------------------------------------------------------- * * Tcl_MacOSXOpenVersionedBundleResources -- * * Given the bundle and version name for a shared library (version name * can be NULL to indicate latest version), this routine sets libraryPath * to the Resources/Scripts directory in the framework package. If * hasResourceFile is true, it will also open the main resource file for * the bundle. * * Results: * TCL_OK if the bundle could be opened, and the Scripts folder found. * TCL_ERROR otherwise. * * Side effects: * libraryVariableName may be set, and the resource file opened. * *---------------------------------------------------------------------- */ int Tcl_MacOSXOpenVersionedBundleResources( Tcl_Interp *interp, const char *bundleName, const char *bundleVersion, int hasResourceFile, int maxPathLen, char *libraryPath) { #ifdef HAVE_COREFOUNDATION CFBundleRef bundleRef, versionedBundleRef = NULL; CFStringRef bundleNameRef; CFURLRef libURL; libraryPath[0] = '\0'; bundleNameRef = CFStringCreateWithCString(NULL, bundleName, kCFStringEncodingUTF8); bundleRef = CFBundleGetBundleWithIdentifier(bundleNameRef); CFRelease(bundleNameRef); if (bundleVersion && bundleRef) { /* * Create bundle from bundleVersion subdirectory of 'Versions'. */ CFURLRef bundleURL = CFBundleCopyBundleURL(bundleRef); if (bundleURL) { CFStringRef bundleVersionRef = CFStringCreateWithCString(NULL, bundleVersion, kCFStringEncodingUTF8); if (bundleVersionRef) { CFComparisonResult versionComparison = kCFCompareLessThan; CFStringRef bundleTailRef = CFURLCopyLastPathComponent( bundleURL); if (bundleTailRef) { versionComparison = CFStringCompare(bundleTailRef, bundleVersionRef, 0); CFRelease(bundleTailRef); } if (versionComparison != kCFCompareEqualTo) { CFURLRef versURL = CFURLCreateCopyAppendingPathComponent( NULL, bundleURL, CFSTR("Versions"), TRUE); if (versURL) { CFURLRef versionedBundleURL = CFURLCreateCopyAppendingPathComponent( NULL, versURL, bundleVersionRef, TRUE); if (versionedBundleURL) { versionedBundleRef = CFBundleCreate(NULL, versionedBundleURL); if (versionedBundleRef) { bundleRef = versionedBundleRef; } CFRelease(versionedBundleURL); } CFRelease(versURL); } } CFRelease(bundleVersionRef); } CFRelease(bundleURL); } } if (bundleRef) { if (hasResourceFile) { (void) OpenResourceMap(bundleRef); } libURL = CFBundleCopyResourceURL(bundleRef, CFSTR("Scripts"), NULL, NULL); if (libURL) { /* * FIXME: This is a quick fix, it is probably not right for * internationalization. */ CFURLGetFileSystemRepresentation(libURL, TRUE, (unsigned char *) libraryPath, maxPathLen); CFRelease(libURL); } if (versionedBundleRef) { #if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 /* * Workaround CFBundle bug in Tiger and earlier. [Bug 2569449] */ if (tclMacOSXDarwinRelease >= 9) #endif { CFRelease(versionedBundleRef); } } } if (libraryPath[0]) { return TCL_OK; } #endif /* HAVE_COREFOUNDATION */ return TCL_ERROR; } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */