]>
Commit | Line | Data |
---|---|---|
2c00a5a8 XL |
1 | /* ===-- os_version_check.c - OS version checking -------------------------=== |
2 | * | |
3 | * The LLVM Compiler Infrastructure | |
4 | * | |
5 | * This file is dual licensed under the MIT and the University of Illinois Open | |
6 | * Source Licenses. See LICENSE.TXT for details. | |
7 | * | |
8 | * ===----------------------------------------------------------------------=== | |
9 | * | |
10 | * This file implements the function __isOSVersionAtLeast, used by | |
11 | * Objective-C's @available | |
12 | * | |
13 | * ===----------------------------------------------------------------------=== | |
14 | */ | |
15 | ||
16 | #ifdef __APPLE__ | |
17 | ||
18 | #include <CoreFoundation/CoreFoundation.h> | |
19 | #include <dispatch/dispatch.h> | |
20 | #include <TargetConditionals.h> | |
21 | #include <dlfcn.h> | |
22 | #include <stdint.h> | |
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
26 | ||
27 | /* These three variables hold the host's OS version. */ | |
28 | static int32_t GlobalMajor, GlobalMinor, GlobalSubminor; | |
29 | static dispatch_once_t DispatchOnceCounter; | |
30 | ||
31 | /* Find and parse the SystemVersion.plist file. */ | |
32 | static void parseSystemVersionPList(void *Unused) { | |
33 | (void)Unused; | |
34 | /* Load CoreFoundation dynamically */ | |
35 | const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull"); | |
36 | if (!NullAllocator) | |
37 | return; | |
38 | const CFAllocatorRef kCFAllocatorNull = | |
39 | *(const CFAllocatorRef *)NullAllocator; | |
40 | typeof(CFDataCreateWithBytesNoCopy) *CFDataCreateWithBytesNoCopyFunc = | |
41 | (typeof(CFDataCreateWithBytesNoCopy) *)dlsym( | |
42 | RTLD_DEFAULT, "CFDataCreateWithBytesNoCopy"); | |
43 | if (!CFDataCreateWithBytesNoCopyFunc) | |
44 | return; | |
45 | typeof(CFPropertyListCreateWithData) *CFPropertyListCreateWithDataFunc = | |
46 | (typeof(CFPropertyListCreateWithData) *)dlsym( | |
47 | RTLD_DEFAULT, "CFPropertyListCreateWithData"); | |
48 | /* CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it | |
49 | * will be NULL on earlier OS versions. */ | |
50 | #pragma clang diagnostic push | |
51 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
52 | typeof(CFPropertyListCreateFromXMLData) *CFPropertyListCreateFromXMLDataFunc = | |
53 | (typeof(CFPropertyListCreateFromXMLData) *)dlsym( | |
54 | RTLD_DEFAULT, "CFPropertyListCreateFromXMLData"); | |
55 | #pragma clang diagnostic pop | |
56 | /* CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it | |
57 | * might be NULL in future OS versions. */ | |
58 | if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc) | |
59 | return; | |
60 | typeof(CFStringCreateWithCStringNoCopy) *CFStringCreateWithCStringNoCopyFunc = | |
61 | (typeof(CFStringCreateWithCStringNoCopy) *)dlsym( | |
62 | RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy"); | |
63 | if (!CFStringCreateWithCStringNoCopyFunc) | |
64 | return; | |
65 | typeof(CFDictionaryGetValue) *CFDictionaryGetValueFunc = | |
66 | (typeof(CFDictionaryGetValue) *)dlsym(RTLD_DEFAULT, | |
67 | "CFDictionaryGetValue"); | |
68 | if (!CFDictionaryGetValueFunc) | |
69 | return; | |
70 | typeof(CFGetTypeID) *CFGetTypeIDFunc = | |
71 | (typeof(CFGetTypeID) *)dlsym(RTLD_DEFAULT, "CFGetTypeID"); | |
72 | if (!CFGetTypeIDFunc) | |
73 | return; | |
74 | typeof(CFStringGetTypeID) *CFStringGetTypeIDFunc = | |
75 | (typeof(CFStringGetTypeID) *)dlsym(RTLD_DEFAULT, "CFStringGetTypeID"); | |
76 | if (!CFStringGetTypeIDFunc) | |
77 | return; | |
78 | typeof(CFStringGetCString) *CFStringGetCStringFunc = | |
79 | (typeof(CFStringGetCString) *)dlsym(RTLD_DEFAULT, "CFStringGetCString"); | |
80 | if (!CFStringGetCStringFunc) | |
81 | return; | |
82 | typeof(CFRelease) *CFReleaseFunc = | |
83 | (typeof(CFRelease) *)dlsym(RTLD_DEFAULT, "CFRelease"); | |
84 | if (!CFReleaseFunc) | |
85 | return; | |
86 | ||
87 | char *PListPath = "/System/Library/CoreServices/SystemVersion.plist"; | |
88 | ||
89 | #if TARGET_OS_SIMULATOR | |
90 | char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT"); | |
91 | if (!PListPathPrefix) | |
92 | return; | |
93 | char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1]; | |
94 | strcpy(FullPath, PListPathPrefix); | |
95 | strcat(FullPath, PListPath); | |
96 | PListPath = FullPath; | |
97 | #endif | |
98 | FILE *PropertyList = fopen(PListPath, "r"); | |
99 | if (!PropertyList) | |
100 | return; | |
101 | ||
102 | /* Dynamically allocated stuff. */ | |
103 | CFDictionaryRef PListRef = NULL; | |
104 | CFDataRef FileContentsRef = NULL; | |
105 | UInt8 *PListBuf = NULL; | |
106 | ||
107 | fseek(PropertyList, 0, SEEK_END); | |
108 | long PListFileSize = ftell(PropertyList); | |
109 | if (PListFileSize < 0) | |
110 | goto Fail; | |
111 | rewind(PropertyList); | |
112 | ||
113 | PListBuf = malloc((size_t)PListFileSize); | |
114 | if (!PListBuf) | |
115 | goto Fail; | |
116 | ||
117 | size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList); | |
118 | if (NumRead != (size_t)PListFileSize) | |
119 | goto Fail; | |
120 | ||
121 | /* Get the file buffer into CF's format. We pass in a null allocator here * | |
122 | * because we free PListBuf ourselves */ | |
123 | FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)( | |
124 | NULL, PListBuf, (CFIndex)NumRead, kCFAllocatorNull); | |
125 | if (!FileContentsRef) | |
126 | goto Fail; | |
127 | ||
128 | if (CFPropertyListCreateWithDataFunc) | |
129 | PListRef = (*CFPropertyListCreateWithDataFunc)( | |
130 | NULL, FileContentsRef, kCFPropertyListImmutable, NULL, NULL); | |
131 | else | |
132 | PListRef = (*CFPropertyListCreateFromXMLDataFunc)( | |
133 | NULL, FileContentsRef, kCFPropertyListImmutable, NULL); | |
134 | if (!PListRef) | |
135 | goto Fail; | |
136 | ||
137 | CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)( | |
138 | NULL, "ProductVersion", kCFStringEncodingASCII, kCFAllocatorNull); | |
139 | if (!ProductVersion) | |
140 | goto Fail; | |
141 | CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion); | |
142 | (*CFReleaseFunc)(ProductVersion); | |
143 | if (!OpaqueValue || | |
144 | (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)()) | |
145 | goto Fail; | |
146 | ||
147 | char VersionStr[32]; | |
148 | if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr, | |
149 | sizeof(VersionStr), kCFStringEncodingUTF8)) | |
150 | goto Fail; | |
151 | sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor); | |
152 | ||
153 | Fail: | |
154 | if (PListRef) | |
155 | (*CFReleaseFunc)(PListRef); | |
156 | if (FileContentsRef) | |
157 | (*CFReleaseFunc)(FileContentsRef); | |
158 | free(PListBuf); | |
159 | fclose(PropertyList); | |
160 | } | |
161 | ||
162 | int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) { | |
163 | /* Populate the global version variables, if they haven't already. */ | |
164 | dispatch_once_f(&DispatchOnceCounter, NULL, parseSystemVersionPList); | |
165 | ||
166 | if (Major < GlobalMajor) return 1; | |
167 | if (Major > GlobalMajor) return 0; | |
168 | if (Minor < GlobalMinor) return 1; | |
169 | if (Minor > GlobalMinor) return 0; | |
170 | return Subminor <= GlobalSubminor; | |
171 | } | |
172 | ||
173 | #else | |
174 | ||
175 | /* Silence an empty translation unit warning. */ | |
176 | typedef int unused; | |
177 | ||
178 | #endif |