/*
* 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 <das@users.sourceforge.net>
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* RCS: @(#) $Id: tclMacOSXBundle.c,v 1.11.4.4 2009/10/05 02:41:13 das Exp $
*/
#include "tclPort.h"
#ifdef HAVE_COREFOUNDATION
#include <CoreFoundation/CoreFoundation.h>
#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 <dlfcn.h>
#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 <mach-o/dyld.h>
#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:
*/
|