/* * 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 #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 #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 #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 #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. * *---------------------------------------------------------------------- */ 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) { /* * Dynamically acquire address for CFBundleOpenBundleResourceMap * symbol, since it is only present in full CoreFoundation on Mac * OS X and not in CFLite on pure Darwin. */ 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 { const char *errMsg = nil; openresourcemap = dlsym(RTLD_NEXT, "CFBundleOpenBundleResourceMap"); if (!openresourcemap) { errMsg = dlerror(); TclLoadDbgMsg("dlsym() failed: %s", errMsg); } } if (!openresourcemap) #endif { #if TCL_DYLD_USE_NSMODULE NSSymbol nsSymbol = NULL; if (NSIsSymbolNameDefinedWithHint( "_CFBundleOpenBundleResourceMap", "CoreFoundation")) { nsSymbol = NSLookupAndBindSymbolWithHint( "_CFBundleOpenBundleResourceMap", "CoreFoundation"); if (nsSymbol) { openresourcemap = NSAddressOfSymbol(nsSymbol); } } #endif } initialized = TRUE; } if (openresourcemap) { short refNum; refNum = 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; } else { return TCL_ERROR; } #else /* HAVE_COREFOUNDATION */ return TCL_ERROR; #endif /* HAVE_COREFOUNDATION */ } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */