1/* 2 * tclMacOSXBundle.c -- 3 * 4 * This file implements functions that inspect CFBundle structures on 5 * MacOS X. 6 * 7 * Copyright 2001, Apple Computer, 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 * The following terms apply to all files originating from Apple 14 * Computer, Inc. ("Apple") and associated with the software unless 15 * explicitly disclaimed in individual files. 16 * 17 * Apple hereby grants permission to use, copy, modify, distribute, and 18 * license this software and its documentation for any purpose, provided 19 * that existing copyright notices are retained in all copies and that 20 * this notice is included verbatim in any distributions. No written 21 * agreement, license, or royalty fee is required for any of the 22 * authorized uses. Modifications to this software may be copyrighted by 23 * their authors and need not follow the licensing terms described here, 24 * provided that the new terms are clearly indicated on the first page of 25 * each file where they apply. 26 * 27 * IN NO EVENT SHALL APPLE, THE AUTHORS OR DISTRIBUTORS OF THE SOFTWARE 28 * BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR 29 * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS 30 * DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF APPLE OR THE 31 * AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. APPLE, 32 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, 33 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 34 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND 35 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND 36 * APPLE,THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE 37 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 38 * 39 * GOVERNMENT USE: If you are acquiring this software on behalf of the 40 * U.S. government, the Government shall have only "Restricted Rights" in 41 * the software and related documentation as defined in the Federal 42 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you are 43 * acquiring the software on behalf of the Department of Defense, the 44 * software shall be classified as "Commercial Computer Software" and the 45 * Government shall have only "Restricted Rights" as defined in Clause 46 * 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the 47 * authors grant the U.S. Government and others acting in its behalf 48 * permission to use and distribute the software in accordance with the 49 * terms specified in this license. 50 * 51 * RCS: @(#) $Id: tclMacOSXBundle.c,v 1.3.2.6 2007/04/29 02:21:33 das Exp $ 52 */ 53 54#include "tclPort.h" 55 56#ifdef HAVE_COREFOUNDATION 57#include <CoreFoundation/CoreFoundation.h> 58#include <mach-o/dyld.h> 59 60#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 61MODULE_SCOPE long tclMacOSXDarwinRelease; 62#endif 63 64#endif /* HAVE_COREFOUNDATION */ 65 66/* 67 *---------------------------------------------------------------------- 68 * 69 * Tcl_MacOSXOpenBundleResources -- 70 * 71 * Given the bundle name for a shared library, this routine sets 72 * libraryPath to the Resources/Scripts directory in the framework 73 * package. If hasResourceFile is true, it will also open the main 74 * resource file for the bundle. 75 * 76 * Results: 77 * TCL_OK if the bundle could be opened, and the Scripts folder found. 78 * TCL_ERROR otherwise. 79 * 80 * Side effects: 81 * libraryVariableName may be set, and the resource file opened. 82 * 83 *---------------------------------------------------------------------- 84 */ 85 86int 87Tcl_MacOSXOpenBundleResources( 88 Tcl_Interp *interp, 89 CONST char *bundleName, 90 int hasResourceFile, 91 int maxPathLen, 92 char *libraryPath) 93{ 94 return Tcl_MacOSXOpenVersionedBundleResources(interp, bundleName, 95 NULL, hasResourceFile, maxPathLen, libraryPath); 96} 97 98/* 99 *---------------------------------------------------------------------- 100 * 101 * Tcl_MacOSXOpenVersionedBundleResources -- 102 * 103 * Given the bundle and version name for a shared library (version name 104 * can be NULL to indicate latest version), this routine sets libraryPath 105 * to the Resources/Scripts directory in the framework package. If 106 * hasResourceFile is true, it will also open the main resource file for 107 * the bundle. 108 * 109 * Results: 110 * TCL_OK if the bundle could be opened, and the Scripts folder found. 111 * TCL_ERROR otherwise. 112 * 113 * Side effects: 114 * libraryVariableName may be set, and the resource file opened. 115 * 116 *---------------------------------------------------------------------- 117 */ 118 119int 120Tcl_MacOSXOpenVersionedBundleResources( 121 Tcl_Interp *interp, 122 CONST char *bundleName, 123 CONST char *bundleVersion, 124 int hasResourceFile, 125 int maxPathLen, 126 char *libraryPath) 127{ 128#ifdef HAVE_COREFOUNDATION 129 CFBundleRef bundleRef, versionedBundleRef = NULL; 130 CFStringRef bundleNameRef; 131 CFURLRef libURL; 132 133 libraryPath[0] = '\0'; 134 135 bundleNameRef = CFStringCreateWithCString(NULL, bundleName, 136 kCFStringEncodingUTF8); 137 138 bundleRef = CFBundleGetBundleWithIdentifier(bundleNameRef); 139 CFRelease(bundleNameRef); 140 141 if (bundleVersion && bundleRef) { 142 /* 143 * Create bundle from bundleVersion subdirectory of 'Versions'. 144 */ 145 146 CFURLRef bundleURL = CFBundleCopyBundleURL(bundleRef); 147 148 if (bundleURL) { 149 CFStringRef bundleVersionRef = CFStringCreateWithCString(NULL, 150 bundleVersion, kCFStringEncodingUTF8); 151 152 if (bundleVersionRef) { 153 CFComparisonResult versionComparison = kCFCompareLessThan; 154 CFStringRef bundleTailRef = CFURLCopyLastPathComponent( 155 bundleURL); 156 157 if (bundleTailRef) { 158 versionComparison = CFStringCompare(bundleTailRef, 159 bundleVersionRef, 0); 160 CFRelease(bundleTailRef); 161 } 162 if (versionComparison != kCFCompareEqualTo) { 163 CFURLRef versURL = CFURLCreateCopyAppendingPathComponent( 164 NULL, bundleURL, CFSTR("Versions"), TRUE); 165 166 if (versURL) { 167 CFURLRef versionedBundleURL = 168 CFURLCreateCopyAppendingPathComponent( 169 NULL, versURL, bundleVersionRef, TRUE); 170 171 if (versionedBundleURL) { 172 versionedBundleRef = CFBundleCreate(NULL, 173 versionedBundleURL); 174 if (versionedBundleRef) { 175 bundleRef = versionedBundleRef; 176 } 177 CFRelease(versionedBundleURL); 178 } 179 CFRelease(versURL); 180 } 181 } 182 CFRelease(bundleVersionRef); 183 } 184 CFRelease(bundleURL); 185 } 186 } 187 188 if (bundleRef) { 189 if (hasResourceFile) { 190 /* 191 * Dynamically acquire address for CFBundleOpenBundleResourceMap 192 * symbol, since it is only present in full CoreFoundation on Mac 193 * OS X and not in CFLite on pure Darwin. 194 */ 195 196 static int initialized = FALSE; 197 static short (*openresourcemap)(CFBundleRef) = NULL; 198 199 if (!initialized) { 200 NSSymbol nsSymbol = NULL; 201 if (NSIsSymbolNameDefinedWithHint( 202 "_CFBundleOpenBundleResourceMap", "CoreFoundation")) { 203 nsSymbol = NSLookupAndBindSymbolWithHint( 204 "_CFBundleOpenBundleResourceMap","CoreFoundation"); 205 if (nsSymbol) { 206 openresourcemap = NSAddressOfSymbol(nsSymbol); 207 } 208 } 209 initialized = TRUE; 210 } 211 212 if (openresourcemap) { 213 short refNum; 214 215 refNum = openresourcemap(bundleRef); 216 } 217 } 218 219 libURL = CFBundleCopyResourceURL(bundleRef, CFSTR("Scripts"), 220 NULL, NULL); 221 222 if (libURL) { 223 /* 224 * FIXME: This is a quick fix, it is probably not right for 225 * internationalization. 226 */ 227 228 CFURLGetFileSystemRepresentation(libURL, TRUE, 229 (unsigned char*) libraryPath, maxPathLen); 230 CFRelease(libURL); 231 } 232 if (versionedBundleRef) { 233#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 234 /* Workaround CFBundle bug in Tiger and earlier. [Bug 2569449] */ 235 if (tclMacOSXDarwinRelease >= 9) 236#endif 237 { 238 CFRelease(versionedBundleRef); 239 } 240 } 241 } 242 243 if (libraryPath[0]) { 244 return TCL_OK; 245 } else { 246 return TCL_ERROR; 247 } 248#else /* HAVE_COREFOUNDATION */ 249 return TCL_ERROR; 250#endif /* HAVE_COREFOUNDATION */ 251} 252