1/* 2 * tclMacOSXBundle.c -- 3 * 4 * This file implements functions that inspect CFBundle structures on 5 * MacOS X. 6 * 7 * Copyright 2001-2009, Apple Inc. 8 * Copyright (c) 2003-2009 Daniel A. Steffen <das@users.sourceforge.net> 9 * 10 * See the file "license.terms" for information on usage and redistribution of 11 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id: tclMacOSXBundle.c,v 1.11.4.4 2009/10/05 02:41:13 das Exp $ 14 */ 15 16#include "tclPort.h" 17 18#ifdef HAVE_COREFOUNDATION 19#include <CoreFoundation/CoreFoundation.h> 20 21#ifndef TCL_DYLD_USE_DLFCN 22/* 23 * Use preferred dlfcn API on 10.4 and later 24 */ 25# if !defined(NO_DLFCN_H) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 26# define TCL_DYLD_USE_DLFCN 1 27# else 28# define TCL_DYLD_USE_DLFCN 0 29# endif 30#endif 31 32#ifndef TCL_DYLD_USE_NSMODULE 33/* 34 * Use deprecated NSModule API only to support 10.3 and earlier: 35 */ 36# if MAC_OS_X_VERSION_MIN_REQUIRED < 1040 37# define TCL_DYLD_USE_NSMODULE 1 38# else 39# define TCL_DYLD_USE_NSMODULE 0 40# endif 41#endif 42 43#if TCL_DYLD_USE_DLFCN 44#include <dlfcn.h> 45#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040 46/* 47 * Support for weakly importing dlfcn API. 48 */ 49extern void *dlsym(void *handle, const char *symbol) WEAK_IMPORT_ATTRIBUTE; 50extern char *dlerror(void) WEAK_IMPORT_ATTRIBUTE; 51#endif 52#endif 53 54#if TCL_DYLD_USE_NSMODULE 55#include <mach-o/dyld.h> 56#endif 57 58#if (TCL_DYLD_USE_DLFCN && MAC_OS_X_VERSION_MIN_REQUIRED < 1040) || \ 59 (MAC_OS_X_VERSION_MIN_REQUIRED < 1050) 60MODULE_SCOPE long tclMacOSXDarwinRelease; 61#endif 62 63#ifdef TCL_DEBUG_LOAD 64#define TclLoadDbgMsg(m, ...) do { \ 65 fprintf(stderr, "%s:%d: %s(): " m ".\n", \ 66 strrchr(__FILE__, '/')+1, __LINE__, __func__, ##__VA_ARGS__); \ 67 } while (0) 68#else 69#define TclLoadDbgMsg(m, ...) 70#endif 71 72#endif /* HAVE_COREFOUNDATION */ 73 74/* 75 *---------------------------------------------------------------------- 76 * 77 * Tcl_MacOSXOpenBundleResources -- 78 * 79 * Given the bundle name for a shared library, this routine sets 80 * libraryPath to the Resources/Scripts directory in the framework 81 * package. If hasResourceFile is true, it will also open the main 82 * resource file for the bundle. 83 * 84 * Results: 85 * TCL_OK if the bundle could be opened, and the Scripts folder found. 86 * TCL_ERROR otherwise. 87 * 88 * Side effects: 89 * libraryVariableName may be set, and the resource file opened. 90 * 91 *---------------------------------------------------------------------- 92 */ 93 94int 95Tcl_MacOSXOpenBundleResources( 96 Tcl_Interp *interp, 97 CONST char *bundleName, 98 int hasResourceFile, 99 int maxPathLen, 100 char *libraryPath) 101{ 102 return Tcl_MacOSXOpenVersionedBundleResources(interp, bundleName, 103 NULL, hasResourceFile, maxPathLen, libraryPath); 104} 105 106/* 107 *---------------------------------------------------------------------- 108 * 109 * Tcl_MacOSXOpenVersionedBundleResources -- 110 * 111 * Given the bundle and version name for a shared library (version name 112 * can be NULL to indicate latest version), this routine sets libraryPath 113 * to the Resources/Scripts directory in the framework package. If 114 * hasResourceFile is true, it will also open the main resource file for 115 * the bundle. 116 * 117 * Results: 118 * TCL_OK if the bundle could be opened, and the Scripts folder found. 119 * TCL_ERROR otherwise. 120 * 121 * Side effects: 122 * libraryVariableName may be set, and the resource file opened. 123 * 124 *---------------------------------------------------------------------- 125 */ 126 127int 128Tcl_MacOSXOpenVersionedBundleResources( 129 Tcl_Interp *interp, 130 CONST char *bundleName, 131 CONST char *bundleVersion, 132 int hasResourceFile, 133 int maxPathLen, 134 char *libraryPath) 135{ 136#ifdef HAVE_COREFOUNDATION 137 CFBundleRef bundleRef, versionedBundleRef = NULL; 138 CFStringRef bundleNameRef; 139 CFURLRef libURL; 140 141 libraryPath[0] = '\0'; 142 143 bundleNameRef = CFStringCreateWithCString(NULL, bundleName, 144 kCFStringEncodingUTF8); 145 146 bundleRef = CFBundleGetBundleWithIdentifier(bundleNameRef); 147 CFRelease(bundleNameRef); 148 149 if (bundleVersion && bundleRef) { 150 /* 151 * Create bundle from bundleVersion subdirectory of 'Versions'. 152 */ 153 154 CFURLRef bundleURL = CFBundleCopyBundleURL(bundleRef); 155 156 if (bundleURL) { 157 CFStringRef bundleVersionRef = CFStringCreateWithCString(NULL, 158 bundleVersion, kCFStringEncodingUTF8); 159 160 if (bundleVersionRef) { 161 CFComparisonResult versionComparison = kCFCompareLessThan; 162 CFStringRef bundleTailRef = CFURLCopyLastPathComponent( 163 bundleURL); 164 165 if (bundleTailRef) { 166 versionComparison = CFStringCompare(bundleTailRef, 167 bundleVersionRef, 0); 168 CFRelease(bundleTailRef); 169 } 170 if (versionComparison != kCFCompareEqualTo) { 171 CFURLRef versURL = CFURLCreateCopyAppendingPathComponent( 172 NULL, bundleURL, CFSTR("Versions"), TRUE); 173 174 if (versURL) { 175 CFURLRef versionedBundleURL = 176 CFURLCreateCopyAppendingPathComponent( 177 NULL, versURL, bundleVersionRef, TRUE); 178 179 if (versionedBundleURL) { 180 versionedBundleRef = CFBundleCreate(NULL, 181 versionedBundleURL); 182 if (versionedBundleRef) { 183 bundleRef = versionedBundleRef; 184 } 185 CFRelease(versionedBundleURL); 186 } 187 CFRelease(versURL); 188 } 189 } 190 CFRelease(bundleVersionRef); 191 } 192 CFRelease(bundleURL); 193 } 194 } 195 196 if (bundleRef) { 197 if (hasResourceFile) { 198 /* 199 * Dynamically acquire address for CFBundleOpenBundleResourceMap 200 * symbol, since it is only present in full CoreFoundation on Mac 201 * OS X and not in CFLite on pure Darwin. 202 */ 203 204 static int initialized = FALSE; 205 static short (*openresourcemap)(CFBundleRef) = NULL; 206 207 if (!initialized) { 208#if TCL_DYLD_USE_DLFCN 209#if MAC_OS_X_VERSION_MIN_REQUIRED < 1040 210 if (tclMacOSXDarwinRelease >= 8) 211#endif 212 { 213 const char *errMsg = nil; 214 openresourcemap = dlsym(RTLD_NEXT, 215 "CFBundleOpenBundleResourceMap"); 216 if (!openresourcemap) { 217 errMsg = dlerror(); 218 TclLoadDbgMsg("dlsym() failed: %s", errMsg); 219 } 220 } 221 if (!openresourcemap) 222#endif 223 { 224#if TCL_DYLD_USE_NSMODULE 225 NSSymbol nsSymbol = NULL; 226 if (NSIsSymbolNameDefinedWithHint( 227 "_CFBundleOpenBundleResourceMap", 228 "CoreFoundation")) { 229 nsSymbol = NSLookupAndBindSymbolWithHint( 230 "_CFBundleOpenBundleResourceMap", 231 "CoreFoundation"); 232 if (nsSymbol) { 233 openresourcemap = NSAddressOfSymbol(nsSymbol); 234 } 235 } 236#endif 237 } 238 initialized = TRUE; 239 } 240 241 if (openresourcemap) { 242 short refNum; 243 244 refNum = openresourcemap(bundleRef); 245 } 246 } 247 248 libURL = CFBundleCopyResourceURL(bundleRef, CFSTR("Scripts"), 249 NULL, NULL); 250 251 if (libURL) { 252 /* 253 * FIXME: This is a quick fix, it is probably not right for 254 * internationalization. 255 */ 256 257 CFURLGetFileSystemRepresentation(libURL, TRUE, 258 (unsigned char*) libraryPath, maxPathLen); 259 CFRelease(libURL); 260 } 261 if (versionedBundleRef) { 262#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 263 /* Workaround CFBundle bug in Tiger and earlier. [Bug 2569449] */ 264 if (tclMacOSXDarwinRelease >= 9) 265#endif 266 { 267 CFRelease(versionedBundleRef); 268 } 269 } 270 } 271 272 if (libraryPath[0]) { 273 return TCL_OK; 274 } else { 275 return TCL_ERROR; 276 } 277#else /* HAVE_COREFOUNDATION */ 278 return TCL_ERROR; 279#endif /* HAVE_COREFOUNDATION */ 280} 281 282/* 283 * Local Variables: 284 * mode: c 285 * c-basic-offset: 4 286 * fill-column: 78 287 * End: 288 */ 289