314 lines
7.7 KiB
C
314 lines
7.7 KiB
C
|
/*
|
|||
|
* 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.
|
|||
|
*/
|
|||
|
|
|||
|
#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 /* 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 <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 /* TCL_DYLD_USE_DLFCN */
|
|||
|
|
|||
|
#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 /* 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 = (short (*)(CFBundleRef))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:
|
|||
|
*/
|