]>
Commit | Line | Data |
---|---|---|
9ce05555 | 1 | /* |
d8925383 | 2 | * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. |
9ce05555 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
9ce05555 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | /* CFUtilities.c | |
24 | Copyright 1998-2002, Apple, Inc. All rights reserved. | |
25 | Responsibility: Christopher Kane | |
26 | */ | |
27 | ||
d8925383 | 28 | #include "CFUtilitiesPriv.h" |
9ce05555 | 29 | #include "CFInternal.h" |
d8925383 | 30 | #include "CFPriv.h" |
9ce05555 A |
31 | #include <CoreFoundation/CFBundle.h> |
32 | #include <CoreFoundation/CFURLAccess.h> | |
33 | #include <CoreFoundation/CFPropertyList.h> | |
34 | #include <CoreFoundation/CFTimeZone.h> | |
35 | #include <math.h> | |
36 | #include <string.h> | |
37 | #include <stdio.h> | |
d8925383 | 38 | #include <stdlib.h> |
9ce05555 A |
39 | #if defined(__MACH__) |
40 | #include <mach/mach.h> | |
41 | #include <pthread.h> | |
9ce05555 | 42 | #include <mach-o/loader.h> |
d8925383 | 43 | #include <mach-o/dyld.h> |
9ce05555 | 44 | #include <crt_externs.h> |
d8925383 | 45 | #include <dlfcn.h> |
9ce05555 | 46 | #include <Security/AuthSession.h> |
d8925383 | 47 | #include <sys/stat.h> |
9ce05555 A |
48 | #endif |
49 | #if defined(__WIN32__) | |
50 | #include <windows.h> | |
51 | #include <process.h> | |
52 | #endif | |
53 | #if defined(__LINUX__) || defined(__FREEBSD__) | |
54 | #include <string.h> | |
55 | #include <pthread.h> | |
9ce05555 A |
56 | #endif |
57 | ||
9ce05555 A |
58 | #define LESS16(A, W) do { if (A < ((uint64_t)1 << (W))) LESS8(A, (W) - 8); LESS8(A, (W) + 8); } while (0) |
59 | #define LESS8(A, W) do { if (A < ((uint64_t)1 << (W))) LESS4(A, (W) - 4); LESS4(A, (W) + 4); } while (0) | |
60 | #define LESS4(A, W) do { if (A < ((uint64_t)1 << (W))) LESS2(A, (W) - 2); LESS2(A, (W) + 2); } while (0) | |
61 | #define LESS2(A, W) do { if (A < ((uint64_t)1 << (W))) LESS1(A, (W) - 1); LESS1(A, (W) + 1); } while (0) | |
62 | #define LESS1(A, W) do { if (A < ((uint64_t)1 << (W))) return (W) - 1; return (W); } while (0) | |
63 | ||
64 | uint32_t CFLog2(uint64_t x) { | |
65 | if (x < ((uint64_t)1 << 32)) | |
66 | LESS16(x, 16); | |
67 | LESS16(x, 48); | |
68 | return 0; | |
69 | } | |
70 | ||
71 | #if 0 | |
72 | // faster version for PPC | |
73 | int Lg2d(unsigned x) { | |
74 | // use PPC-specific instruction to count leading zeros | |
75 | int ret; | |
76 | if (0 == x) return 0; | |
77 | __asm__ volatile("cntlzw %0,%1" : "=r" (ret) : "r" (x)); | |
78 | return 31 - ret; | |
79 | } | |
80 | #endif | |
81 | ||
82 | #undef LESS1 | |
83 | #undef LESS2 | |
84 | #undef LESS4 | |
85 | #undef LESS8 | |
86 | #undef LESS16 | |
87 | ||
88 | /* Comparator is passed the address of the values. */ | |
89 | /* Binary searches a sorted-increasing array of some type. | |
90 | Return value is either 1) the index of the element desired, | |
91 | if the target value exists in the list, 2) greater than or | |
92 | equal to count, if the element is greater than all the values | |
93 | in the list, or 3) the index of the element greater than the | |
94 | target value. | |
95 | ||
96 | For example, a search in the list of integers: | |
97 | 2 3 5 7 11 13 17 | |
98 | ||
99 | For... Will Return... | |
100 | 2 0 | |
101 | 5 2 | |
102 | 23 7 | |
103 | 1 0 | |
104 | 9 4 | |
105 | ||
106 | For instance, if you just care about found/not found: | |
107 | index = CFBSearch(list, count, elem); | |
108 | if (count <= index || list[index] != elem) { | |
109 | * Not found * | |
110 | } else { | |
111 | * Found * | |
112 | } | |
113 | ||
114 | This isn't optimal yet. | |
115 | */ | |
116 | __private_extern__ CFIndex CFBSearch(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) { | |
117 | SInt32 idx, lg; | |
118 | const char *ptr = (const char *)list; | |
119 | if (count < 4) { | |
120 | switch (count) { | |
121 | case 3: if (comparator(ptr + elementSize * 2, element, context) < 0) return 3; | |
122 | case 2: if (comparator(ptr + elementSize * 1, element, context) < 0) return 2; | |
123 | case 1: if (comparator(ptr + elementSize * 0, element, context) < 0) return 1; | |
124 | } | |
125 | return 0; | |
126 | } | |
127 | if (comparator(ptr + elementSize * (count - 1), element, context) < 0) return count; | |
128 | if (comparator(element, ptr + elementSize * 0, context) < 0) return 0; | |
129 | lg = CFLog2(count); /* This takes about 1/3rd of the time, discounting calls to comparator */ | |
130 | idx = (comparator(ptr + elementSize * (-1 + (1 << lg)), element, context) < 0) ? count - (1 << lg) : -1; | |
131 | switch (--lg) { | |
132 | case 30: if (comparator(ptr + elementSize * (idx + (1 << 30)), element, context) < 0) idx += (1 << 30); | |
133 | case 29: if (comparator(ptr + elementSize * (idx + (1 << 29)), element, context) < 0) idx += (1 << 29); | |
134 | case 28: if (comparator(ptr + elementSize * (idx + (1 << 28)), element, context) < 0) idx += (1 << 28); | |
135 | case 27: if (comparator(ptr + elementSize * (idx + (1 << 27)), element, context) < 0) idx += (1 << 27); | |
136 | case 26: if (comparator(ptr + elementSize * (idx + (1 << 26)), element, context) < 0) idx += (1 << 26); | |
137 | case 25: if (comparator(ptr + elementSize * (idx + (1 << 25)), element, context) < 0) idx += (1 << 25); | |
138 | case 24: if (comparator(ptr + elementSize * (idx + (1 << 24)), element, context) < 0) idx += (1 << 24); | |
139 | case 23: if (comparator(ptr + elementSize * (idx + (1 << 23)), element, context) < 0) idx += (1 << 23); | |
140 | case 22: if (comparator(ptr + elementSize * (idx + (1 << 22)), element, context) < 0) idx += (1 << 22); | |
141 | case 21: if (comparator(ptr + elementSize * (idx + (1 << 21)), element, context) < 0) idx += (1 << 21); | |
142 | case 20: if (comparator(ptr + elementSize * (idx + (1 << 20)), element, context) < 0) idx += (1 << 20); | |
143 | case 19: if (comparator(ptr + elementSize * (idx + (1 << 19)), element, context) < 0) idx += (1 << 19); | |
144 | case 18: if (comparator(ptr + elementSize * (idx + (1 << 18)), element, context) < 0) idx += (1 << 18); | |
145 | case 17: if (comparator(ptr + elementSize * (idx + (1 << 17)), element, context) < 0) idx += (1 << 17); | |
146 | case 16: if (comparator(ptr + elementSize * (idx + (1 << 16)), element, context) < 0) idx += (1 << 16); | |
147 | case 15: if (comparator(ptr + elementSize * (idx + (1 << 15)), element, context) < 0) idx += (1 << 15); | |
148 | case 14: if (comparator(ptr + elementSize * (idx + (1 << 14)), element, context) < 0) idx += (1 << 14); | |
149 | case 13: if (comparator(ptr + elementSize * (idx + (1 << 13)), element, context) < 0) idx += (1 << 13); | |
150 | case 12: if (comparator(ptr + elementSize * (idx + (1 << 12)), element, context) < 0) idx += (1 << 12); | |
151 | case 11: if (comparator(ptr + elementSize * (idx + (1 << 11)), element, context) < 0) idx += (1 << 11); | |
152 | case 10: if (comparator(ptr + elementSize * (idx + (1 << 10)), element, context) < 0) idx += (1 << 10); | |
153 | case 9: if (comparator(ptr + elementSize * (idx + (1 << 9)), element, context) < 0) idx += (1 << 9); | |
154 | case 8: if (comparator(ptr + elementSize * (idx + (1 << 8)), element, context) < 0) idx += (1 << 8); | |
155 | case 7: if (comparator(ptr + elementSize * (idx + (1 << 7)), element, context) < 0) idx += (1 << 7); | |
156 | case 6: if (comparator(ptr + elementSize * (idx + (1 << 6)), element, context) < 0) idx += (1 << 6); | |
157 | case 5: if (comparator(ptr + elementSize * (idx + (1 << 5)), element, context) < 0) idx += (1 << 5); | |
158 | case 4: if (comparator(ptr + elementSize * (idx + (1 << 4)), element, context) < 0) idx += (1 << 4); | |
159 | case 3: if (comparator(ptr + elementSize * (idx + (1 << 3)), element, context) < 0) idx += (1 << 3); | |
160 | case 2: if (comparator(ptr + elementSize * (idx + (1 << 2)), element, context) < 0) idx += (1 << 2); | |
161 | case 1: if (comparator(ptr + elementSize * (idx + (1 << 1)), element, context) < 0) idx += (1 << 1); | |
162 | case 0: if (comparator(ptr + elementSize * (idx + (1 << 0)), element, context) < 0) idx += (1 << 0); | |
163 | } | |
164 | return ++idx; | |
165 | } | |
166 | ||
167 | ||
168 | #define ELF_STEP(B) T1 = (H << 4) + B; T2 = T1 & 0xF0000000; if (T2) T1 ^= (T2 >> 24); T1 &= (~T2); H = T1; | |
169 | ||
170 | CFHashCode CFHashBytes(uint8_t *bytes, CFIndex length) { | |
171 | /* The ELF hash algorithm, used in the ELF object file format */ | |
172 | UInt32 H = 0, T1, T2; | |
173 | SInt32 rem = length; | |
174 | while (3 < rem) { | |
175 | ELF_STEP(bytes[length - rem]); | |
176 | ELF_STEP(bytes[length - rem + 1]); | |
177 | ELF_STEP(bytes[length - rem + 2]); | |
178 | ELF_STEP(bytes[length - rem + 3]); | |
179 | rem -= 4; | |
180 | } | |
181 | switch (rem) { | |
182 | case 3: ELF_STEP(bytes[length - 3]); | |
183 | case 2: ELF_STEP(bytes[length - 2]); | |
184 | case 1: ELF_STEP(bytes[length - 1]); | |
185 | case 0: ; | |
186 | } | |
187 | return H; | |
188 | } | |
189 | ||
190 | #undef ELF_STEP | |
191 | ||
192 | #if defined(__WIN32__) | |
193 | struct _args { | |
194 | void *func; | |
195 | void *arg; | |
196 | HANDLE handle; | |
197 | }; | |
198 | static __stdcall unsigned __CFWinThreadFunc(void *arg) { | |
199 | struct _args *args = arg; | |
200 | ((void (*)(void *))args->func)(args->arg); | |
201 | CloseHandle(args->handle); | |
202 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, arg); | |
203 | _endthreadex(0); | |
204 | return 0; | |
205 | } | |
206 | #endif | |
207 | ||
208 | __private_extern__ void *__CFStartSimpleThread(void *func, void *arg) { | |
209 | #if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) | |
210 | pthread_attr_t attr; | |
211 | pthread_t tid; | |
212 | pthread_attr_init(&attr); | |
213 | pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); | |
214 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | |
215 | pthread_attr_setstacksize(&attr, 60 * 1024); // 60K stack for our internal threads is sufficient | |
216 | pthread_create(&tid, &attr, func, arg); | |
217 | pthread_attr_destroy(&attr); | |
218 | //warning CF: we dont actually know that a pthread_t is the same size as void * | |
219 | return (void *)tid; | |
220 | #else | |
221 | unsigned tid; | |
222 | struct _args *args = CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(struct _args), 0); | |
223 | if (__CFOASafe) __CFSetLastAllocationEventName(args, "CFUtilities (thread-args)"); | |
224 | HANDLE handle; | |
225 | args->func = func; | |
226 | args->arg = arg; | |
227 | /* The thread is created suspended, because otherwise there would be a race between the assignment below of the handle field, and it's possible use in the thread func above. */ | |
228 | args->handle = (HANDLE)_beginthreadex(NULL, 0, (void *)__CFWinThreadFunc, args, CREATE_SUSPENDED, &tid); | |
229 | handle = args->handle; | |
230 | ResumeThread(handle); | |
231 | return handle; | |
232 | #endif | |
233 | } | |
234 | ||
235 | __private_extern__ CFStringRef _CFCreateLimitedUniqueString() { | |
236 | /* this unique string is only unique to the current host during the current boot */ | |
237 | uint64_t tsr = __CFReadTSR(); | |
238 | UInt32 tsrh = (tsr >> 32), tsrl = (tsr & (int64_t)0xFFFFFFFF); | |
239 | return CFStringCreateWithFormat(NULL, NULL, CFSTR("CFUniqueString-%lu%lu$"), tsrh, tsrl); | |
240 | } | |
241 | ||
242 | ||
243 | // Looks for localized version of "nonLocalized" in the SystemVersion bundle | |
244 | // If not found, and returnNonLocalizedFlag == true, will return the non localized string (retained of course), otherwise NULL | |
245 | // If bundlePtr != NULL, will use *bundlePtr and will return the bundle in there; otherwise bundle is created and released | |
246 | ||
247 | static CFStringRef _CFCopyLocalizedVersionKey(CFBundleRef *bundlePtr, CFStringRef nonLocalized) { | |
248 | CFStringRef localized = NULL; | |
249 | CFBundleRef locBundle = bundlePtr ? *bundlePtr : NULL; | |
250 | if (!locBundle) { | |
251 | CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR("/System/Library/CoreServices/SystemVersion.bundle"), kCFURLPOSIXPathStyle, false); | |
252 | if (url) { | |
253 | locBundle = CFBundleCreate(kCFAllocatorDefault, url); | |
254 | CFRelease(url); | |
255 | } | |
256 | } | |
257 | if (locBundle) { | |
258 | localized = CFBundleCopyLocalizedString(locBundle, nonLocalized, nonLocalized, CFSTR("SystemVersion")); | |
259 | if (bundlePtr) *bundlePtr = locBundle; else CFRelease(locBundle); | |
260 | } | |
261 | return localized ? localized : CFRetain(nonLocalized); | |
262 | } | |
263 | ||
9ce05555 A |
264 | static CFDictionaryRef _CFCopyVersionDictionary(CFStringRef path) { |
265 | CFPropertyListRef plist = NULL; | |
266 | CFDataRef data; | |
267 | CFURLRef url; | |
268 | ||
269 | url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false); | |
270 | if (url && CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &data, NULL, NULL, NULL)) { | |
271 | plist = CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListMutableContainers, NULL); | |
272 | CFRelease(data); | |
273 | } | |
274 | if (url) CFRelease(url); | |
275 | ||
276 | if (plist) { | |
277 | CFBundleRef locBundle = NULL; | |
278 | CFStringRef fullVersion, vers, versExtra, build; | |
279 | CFStringRef versionString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionProductVersionStringKey); | |
280 | CFStringRef buildString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionBuildStringKey); | |
281 | CFStringRef fullVersionString = _CFCopyLocalizedVersionKey(&locBundle, CFSTR("FullVersionString")); | |
282 | if (locBundle) CFRelease(locBundle); | |
283 | ||
284 | // Now build the full version string | |
285 | if (CFEqual(fullVersionString, CFSTR("FullVersionString"))) { | |
286 | CFRelease(fullVersionString); | |
287 | fullVersionString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ %%@ (%@ %%@)"), versionString, buildString); | |
288 | } | |
289 | vers = CFDictionaryGetValue(plist, _kCFSystemVersionProductVersionKey); | |
290 | versExtra = CFDictionaryGetValue(plist, _kCFSystemVersionProductVersionExtraKey); | |
291 | if (vers && versExtra) vers = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ %@"), vers, versExtra); | |
292 | build = CFDictionaryGetValue(plist, _kCFSystemVersionBuildVersionKey); | |
293 | fullVersion = CFStringCreateWithFormat(NULL, NULL, fullVersionString, (vers ? vers : CFSTR("?")), build ? build : CFSTR("?")); | |
294 | if (vers && versExtra) CFRelease(vers); | |
295 | ||
296 | CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductVersionStringKey, versionString); | |
297 | CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionBuildStringKey, buildString); | |
298 | CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR("FullVersionString"), fullVersion); | |
299 | CFRelease(versionString); | |
300 | CFRelease(buildString); | |
301 | CFRelease(fullVersionString); | |
302 | CFRelease(fullVersion); | |
303 | } | |
304 | return plist; | |
305 | } | |
306 | ||
307 | CFStringRef CFCopySystemVersionString(void) { | |
308 | CFStringRef versionString; | |
309 | CFDictionaryRef dict = _CFCopyServerVersionDictionary(); | |
310 | if (!dict) dict = _CFCopySystemVersionDictionary(); | |
311 | versionString = CFDictionaryGetValue(dict, CFSTR("FullVersionString")); | |
312 | if (versionString) CFRetain(versionString); | |
313 | CFRelease(dict); | |
314 | return versionString; | |
315 | } | |
316 | ||
d8925383 A |
317 | // Obsolete: These two functions cache the dictionaries to avoid calling _CFCopyVersionDictionary() more than once per dict desired |
318 | // In fact, they do not cache any more, because the file can change after | |
319 | // apps are running in some situations, and apps need the new info. | |
320 | // Proper caching and testing to see if the file has changed, without race | |
321 | // conditions, would require semi-convoluted use of fstat(). | |
9ce05555 A |
322 | |
323 | CFDictionaryRef _CFCopySystemVersionDictionary(void) { | |
d8925383 | 324 | CFPropertyListRef plist = NULL; |
9ce05555 | 325 | plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/SystemVersion.plist")); |
d8925383 | 326 | return plist; |
9ce05555 A |
327 | } |
328 | ||
329 | CFDictionaryRef _CFCopyServerVersionDictionary(void) { | |
d8925383 | 330 | CFPropertyListRef plist = NULL; |
9ce05555 | 331 | plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/ServerVersion.plist")); |
d8925383 | 332 | return plist; |
9ce05555 A |
333 | } |
334 | ||
335 | CONST_STRING_DECL(_kCFSystemVersionProductNameKey, "ProductName") | |
336 | CONST_STRING_DECL(_kCFSystemVersionProductCopyrightKey, "ProductCopyright") | |
337 | CONST_STRING_DECL(_kCFSystemVersionProductVersionKey, "ProductVersion") | |
338 | CONST_STRING_DECL(_kCFSystemVersionProductVersionExtraKey, "ProductVersionExtra") | |
339 | CONST_STRING_DECL(_kCFSystemVersionProductUserVisibleVersionKey, "ProductUserVisibleVersion") | |
340 | CONST_STRING_DECL(_kCFSystemVersionBuildVersionKey, "ProductBuildVersion") | |
341 | CONST_STRING_DECL(_kCFSystemVersionProductVersionStringKey, "Version") | |
342 | CONST_STRING_DECL(_kCFSystemVersionBuildStringKey, "Build") | |
343 | ||
344 | #if defined(__MACH__) | |
d8925383 A |
345 | |
346 | typedef struct { | |
347 | uint16_t primaryVersion; | |
348 | uint8_t secondaryVersion; | |
349 | uint8_t tertiaryVersion; | |
350 | } CFLibraryVersion; | |
351 | ||
9ce05555 A |
352 | CFLibraryVersion CFGetExecutableLinkedLibraryVersion(CFStringRef libraryName) { |
353 | CFLibraryVersion ret = {0xFFFF, 0xFF, 0xFF}; | |
9ce05555 | 354 | char library[CFMaxPathSize]; // search specs larger than this are pointless |
9ce05555 | 355 | if (!CFStringGetCString(libraryName, library, sizeof(library), kCFStringEncodingUTF8)) return ret; |
d8925383 A |
356 | int32_t version = NSVersionOfLinkTimeLibrary(library); |
357 | if (-1 != version) { | |
358 | ret.primaryVersion = version >> 16; | |
359 | ret.secondaryVersion = (version >> 8) & 0xff; | |
360 | ret.tertiaryVersion = version & 0xff; | |
9ce05555 A |
361 | } |
362 | return ret; | |
363 | } | |
364 | ||
365 | CFLibraryVersion CFGetExecutingLibraryVersion(CFStringRef libraryName) { | |
366 | CFLibraryVersion ret = {0xFFFF, 0xFF, 0xFF}; | |
9ce05555 | 367 | char library[CFMaxPathSize]; // search specs larger than this are pointless |
9ce05555 | 368 | if (!CFStringGetCString(libraryName, library, sizeof(library), kCFStringEncodingUTF8)) return ret; |
d8925383 A |
369 | int32_t version = NSVersionOfRunTimeLibrary(library); |
370 | if (-1 != version) { | |
371 | ret.primaryVersion = version >> 16; | |
372 | ret.secondaryVersion = (version >> 8) & 0xff; | |
373 | ret.tertiaryVersion = version & 0xff; | |
9ce05555 A |
374 | } |
375 | return ret; | |
376 | } | |
377 | ||
378 | ||
379 | /* | |
380 | If | |
381 | (vers != 0xFFFF): We know the version number of the library this app was linked against | |
382 | and (versionInfo[version].VERSIONFIELD != 0xFFFF): And we know what version number started the specified release | |
383 | and ((version == 0) || (versionInfo[version-1].VERSIONFIELD < versionInfo[version].VERSIONFIELD)): And it's distinct from the prev release | |
384 | Then | |
385 | If the version the app is linked against is less than the version recorded for the specified release | |
386 | Then stop checking and return false | |
387 | Else stop checking and return YES | |
388 | Else | |
389 | Continue checking (the next library) | |
390 | */ | |
391 | #define checkLibrary(LIBNAME, VERSIONFIELD) \ | |
d8925383 | 392 | {uint16_t vers = (NSVersionOfLinkTimeLibrary(LIBNAME) >> 16); \ |
9ce05555 A |
393 | if ((vers != 0xFFFF) && (versionInfo[version].VERSIONFIELD != 0xFFFF) && ((version == 0) || (versionInfo[version-1].VERSIONFIELD < versionInfo[version].VERSIONFIELD))) return (results[version] = ((vers < versionInfo[version].VERSIONFIELD) ? false : true)); } |
394 | ||
395 | CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version) { | |
396 | // The numbers in the below table should be the numbers for any version of the framework in the release. | |
397 | // When adding new entries to this table for a new build train, it's simplest to use the versions of the | |
398 | // first new versions of projects submitted to the new train. These can later be updated. One thing to watch for is that software updates | |
399 | // for the previous release do not increase numbers beyond the number used for the next release! | |
400 | // For a given train, don't ever use the last versions submitted to the previous train! (This to assure room for software updates.) | |
401 | // If versions are the same as previous release, use 0xFFFF; this will assure the answer is a conservative NO. | |
402 | // NOTE: Also update the CFM check below, perhaps to the previous release... (???) | |
403 | static const struct { | |
404 | uint16_t libSystemVersion; | |
405 | uint16_t cocoaVersion; | |
406 | uint16_t appkitVersion; | |
407 | uint16_t fouVersion; | |
408 | uint16_t cfVersion; | |
409 | uint16_t carbonVersion; | |
410 | uint16_t applicationServicesVersion; | |
411 | uint16_t coreServicesVersion; | |
412 | uint16_t iokitVersion; | |
413 | } versionInfo[] = { | |
414 | {50, 5, 577, 397, 196, 113, 16, 9, 52}, /* CFSystemVersionCheetah (used the last versions) */ | |
415 | {55, 7, 620, 425, 226, 122, 16, 10, 67}, /* CFSystemVersionPuma (used the last versions) */ | |
416 | {56, 8, 631, 431, 232, 122, 17, 11, 73}, /* CFSystemVersionJaguar */ | |
417 | {67, 9, 704, 481, 281, 126, 19, 16, 159}, /* CFSystemVersionPanther */ | |
d8925383 A |
418 | {73, 10, 750, 505, 305, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, /* CFSystemVersionTiger */ |
419 | {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, /* CFSystemVersionChablis */ | |
9ce05555 | 420 | }; |
d8925383 | 421 | static char results[CFSystemVersionMax] = {-2, -2, -2, -2, -2, -2}; /* We cache the results per-release; there are only a few of these... */ |
9ce05555 A |
422 | if (version >= CFSystemVersionMax) return false; /* Actually, we don't know the answer, and something scary is going on */ |
423 | if (results[version] != -2) return results[version]; | |
424 | ||
425 | if (_CFIsCFM()) { | |
426 | results[version] = (version <= CFSystemVersionJaguar) ? true : false; | |
427 | return results[version]; | |
428 | } | |
429 | ||
d8925383 A |
430 | checkLibrary("System", libSystemVersion); // Pretty much everyone links with this |
431 | checkLibrary("Cocoa", cocoaVersion); | |
432 | checkLibrary("AppKit", appkitVersion); | |
433 | checkLibrary("Foundation", fouVersion); | |
434 | checkLibrary("CoreFoundation", cfVersion); | |
435 | checkLibrary("Carbon", carbonVersion); | |
436 | checkLibrary("ApplicationServices", applicationServicesVersion); | |
437 | checkLibrary("CoreServices", coreServicesVersion); | |
438 | checkLibrary("IOKit", iokitVersion); | |
9ce05555 A |
439 | |
440 | /* If not found, then simply return NO to indicate earlier --- compatibility by default, unfortunately */ | |
441 | return false; | |
442 | } | |
d8925383 A |
443 | #else |
444 | CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version) { | |
445 | return true; | |
446 | } | |
9ce05555 A |
447 | #endif |
448 | ||
449 | ||
d8925383 A |
450 | __private_extern__ void *__CFLookupCarbonCoreFunction(const char *name) { |
451 | static void *image = NULL; | |
452 | if (NULL == image) { | |
453 | image = dlopen("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", RTLD_LAZY | RTLD_LOCAL); | |
454 | } | |
455 | void *dyfunc = NULL; | |
456 | if (image) { | |
457 | dyfunc = dlsym(image, name); | |
458 | } | |
459 | return dyfunc; | |
460 | } | |
461 | ||
462 | __private_extern__ void *__CFLookupCFNetworkFunction(const char *name) { | |
463 | static void *image = NULL; | |
464 | if (NULL == image) { | |
465 | image = dlopen("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CFNetwork.framework/Versions/A/CFNetwork", RTLD_LAZY | RTLD_LOCAL); | |
466 | } | |
467 | void *dyfunc = NULL; | |
468 | if (image) { | |
469 | dyfunc = dlsym(image, name); | |
470 | } | |
471 | return dyfunc; | |
472 | } | |
9ce05555 A |
473 | |
474 | ||
d8925383 A |
475 | |
476 | static void _CFShowToFile(FILE *file, Boolean flush, const void *obj) { | |
9ce05555 A |
477 | CFStringRef str; |
478 | CFIndex idx, cnt; | |
479 | CFStringInlineBuffer buffer; | |
480 | bool lastNL = false; | |
481 | ||
482 | if (obj) { | |
483 | if (CFGetTypeID(obj) == CFStringGetTypeID()) { | |
484 | // Makes Ali marginally happier | |
485 | str = __CFCopyFormattingDescription(obj, NULL); | |
486 | if (!str) str = CFCopyDescription(obj); | |
487 | } else { | |
488 | str = CFCopyDescription(obj); | |
489 | } | |
490 | } else { | |
491 | str = CFRetain(CFSTR("(null)")); | |
492 | } | |
493 | cnt = CFStringGetLength(str); | |
494 | ||
d8925383 A |
495 | // iTunes used OutputDebugStringW(theString); |
496 | ||
9ce05555 A |
497 | CFStringInitInlineBuffer(str, &buffer, CFRangeMake(0, cnt)); |
498 | for (idx = 0; idx < cnt; idx++) { | |
499 | UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&buffer, idx); | |
500 | if (ch < 128) { | |
d8925383 | 501 | fprintf(file, "%c", ch); |
9ce05555 A |
502 | lastNL = (ch == '\n'); |
503 | } else { | |
d8925383 | 504 | fprintf(file, "\\u%04x", ch); |
9ce05555 A |
505 | } |
506 | } | |
d8925383 A |
507 | if (!lastNL) { |
508 | fprintf(file, "\n"); | |
509 | if (flush) fflush(file); | |
510 | } | |
9ce05555 A |
511 | |
512 | if (str) CFRelease(str); | |
513 | } | |
514 | ||
d8925383 A |
515 | void CFShow(const void *obj) { |
516 | _CFShowToFile(stderr, true, obj); | |
517 | } | |
518 | ||
519 | static CFGregorianDate gregorianDate(void) { | |
520 | CFTimeZoneRef tz = CFTimeZoneCopySystem(); // specifically choose system time zone for logs | |
521 | CFGregorianDate gdate = CFAbsoluteTimeGetGregorianDate(CFAbsoluteTimeGetCurrent(), tz); | |
522 | CFRelease(tz); | |
523 | gdate.second = gdate.second + 0.0005; | |
524 | return gdate; | |
525 | } | |
526 | ||
9ce05555 A |
527 | void CFLog(int p, CFStringRef format, ...) { |
528 | CFStringRef result; | |
529 | va_list argList; | |
530 | static CFSpinLock_t lock = 0; | |
531 | ||
532 | va_start(argList, format); | |
533 | result = CFStringCreateWithFormatAndArguments(NULL, NULL, format, argList); | |
534 | va_end(argList); | |
535 | ||
536 | __CFSpinLock(&lock); | |
537 | #if defined(__WIN32__) | |
d8925383 | 538 | fprintf(stderr, "*** %s[%ld] CFLog(%d): ", *_CFGetProgname(), GetCurrentProcessId(), p); |
9ce05555 | 539 | #else |
d8925383 A |
540 | CFGregorianDate gdate = gregorianDate(); |
541 | // Date format: YYYY '-' MM '-' DD ' ' hh ':' mm ':' ss.fff | |
542 | fprintf_l(stderr, NULL, "%04d-%02d-%02d %02d:%02d:%06.3f %s[%d] CFLog (%d): ", (int)gdate.year, gdate.month, gdate.day, gdate.hour, gdate.minute, gdate.second, *_CFGetProgname(), getpid(), p); | |
9ce05555 A |
543 | #endif |
544 | ||
545 | CFShow(result); | |
546 | __CFSpinUnlock(&lock); | |
547 | CFRelease(result); | |
548 | } | |
549 | ||
d8925383 | 550 |