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 <AvailabilityMacros.h> |
---|
19 | #include <CoreFoundation/CoreFoundation.h> |
---|
20 | #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 |
---|
21 | #include <dispatch/dispatch.h> |
---|
22 | #endif |
---|
23 | #include <TargetConditionals.h> |
---|
24 | #include <dlfcn.h> |
---|
25 | #include <stdint.h> |
---|
26 | #include <stdio.h> |
---|
27 | #include <stdlib.h> |
---|
28 | #include <string.h> |
---|
29 | |
---|
30 | /* These three variables hold the host's OS version. */ |
---|
31 | static int32_t GlobalMajor, GlobalMinor, GlobalSubminor; |
---|
32 | #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 |
---|
33 | static dispatch_once_t DispatchOnceCounter; |
---|
34 | #endif |
---|
35 | |
---|
36 | #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 |
---|
37 | /* declare a missing reference not found in SDK < 10.6 for function called below */ |
---|
38 | typedef struct __CFError * CFErrorRef; |
---|
39 | extern CFPropertyListRef CFPropertyListCreateWithData(CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *, CFErrorRef *); |
---|
40 | #endif |
---|
41 | |
---|
42 | /* Find and parse the SystemVersion.plist file. */ |
---|
43 | static void parseSystemVersionPList(void *Unused) { |
---|
44 | (void)Unused; |
---|
45 | /* Load CoreFoundation dynamically */ |
---|
46 | const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull"); |
---|
47 | if (!NullAllocator) |
---|
48 | return; |
---|
49 | const CFAllocatorRef kCFAllocatorNull = |
---|
50 | *(const CFAllocatorRef *)NullAllocator; |
---|
51 | typeof(CFDataCreateWithBytesNoCopy) *CFDataCreateWithBytesNoCopyFunc = |
---|
52 | (typeof(CFDataCreateWithBytesNoCopy) *)dlsym( |
---|
53 | RTLD_DEFAULT, "CFDataCreateWithBytesNoCopy"); |
---|
54 | if (!CFDataCreateWithBytesNoCopyFunc) |
---|
55 | return; |
---|
56 | typeof(CFPropertyListCreateWithData) *CFPropertyListCreateWithDataFunc = |
---|
57 | (typeof(CFPropertyListCreateWithData) *)dlsym( |
---|
58 | RTLD_DEFAULT, "CFPropertyListCreateWithData"); |
---|
59 | /* CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it |
---|
60 | * will be NULL on earlier OS versions. */ |
---|
61 | #pragma clang diagnostic push |
---|
62 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
---|
63 | typeof(CFPropertyListCreateFromXMLData) *CFPropertyListCreateFromXMLDataFunc = |
---|
64 | (typeof(CFPropertyListCreateFromXMLData) *)dlsym( |
---|
65 | RTLD_DEFAULT, "CFPropertyListCreateFromXMLData"); |
---|
66 | #pragma clang diagnostic pop |
---|
67 | /* CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it |
---|
68 | * might be NULL in future OS versions. */ |
---|
69 | if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc) |
---|
70 | return; |
---|
71 | typeof(CFStringCreateWithCStringNoCopy) *CFStringCreateWithCStringNoCopyFunc = |
---|
72 | (typeof(CFStringCreateWithCStringNoCopy) *)dlsym( |
---|
73 | RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy"); |
---|
74 | if (!CFStringCreateWithCStringNoCopyFunc) |
---|
75 | return; |
---|
76 | typeof(CFDictionaryGetValue) *CFDictionaryGetValueFunc = |
---|
77 | (typeof(CFDictionaryGetValue) *)dlsym(RTLD_DEFAULT, |
---|
78 | "CFDictionaryGetValue"); |
---|
79 | if (!CFDictionaryGetValueFunc) |
---|
80 | return; |
---|
81 | typeof(CFGetTypeID) *CFGetTypeIDFunc = |
---|
82 | (typeof(CFGetTypeID) *)dlsym(RTLD_DEFAULT, "CFGetTypeID"); |
---|
83 | if (!CFGetTypeIDFunc) |
---|
84 | return; |
---|
85 | typeof(CFStringGetTypeID) *CFStringGetTypeIDFunc = |
---|
86 | (typeof(CFStringGetTypeID) *)dlsym(RTLD_DEFAULT, "CFStringGetTypeID"); |
---|
87 | if (!CFStringGetTypeIDFunc) |
---|
88 | return; |
---|
89 | typeof(CFStringGetCString) *CFStringGetCStringFunc = |
---|
90 | (typeof(CFStringGetCString) *)dlsym(RTLD_DEFAULT, "CFStringGetCString"); |
---|
91 | if (!CFStringGetCStringFunc) |
---|
92 | return; |
---|
93 | typeof(CFRelease) *CFReleaseFunc = |
---|
94 | (typeof(CFRelease) *)dlsym(RTLD_DEFAULT, "CFRelease"); |
---|
95 | if (!CFReleaseFunc) |
---|
96 | return; |
---|
97 | |
---|
98 | char *PListPath = "/System/Library/CoreServices/SystemVersion.plist"; |
---|
99 | |
---|
100 | #if TARGET_OS_SIMULATOR |
---|
101 | char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT"); |
---|
102 | if (!PListPathPrefix) |
---|
103 | return; |
---|
104 | char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1]; |
---|
105 | strcpy(FullPath, PListPathPrefix); |
---|
106 | strcat(FullPath, PListPath); |
---|
107 | PListPath = FullPath; |
---|
108 | #endif |
---|
109 | FILE *PropertyList = fopen(PListPath, "r"); |
---|
110 | if (!PropertyList) |
---|
111 | return; |
---|
112 | |
---|
113 | /* Dynamically allocated stuff. */ |
---|
114 | CFDictionaryRef PListRef = NULL; |
---|
115 | CFDataRef FileContentsRef = NULL; |
---|
116 | UInt8 *PListBuf = NULL; |
---|
117 | |
---|
118 | fseek(PropertyList, 0, SEEK_END); |
---|
119 | long PListFileSize = ftell(PropertyList); |
---|
120 | if (PListFileSize < 0) |
---|
121 | goto Fail; |
---|
122 | rewind(PropertyList); |
---|
123 | |
---|
124 | PListBuf = malloc((size_t)PListFileSize); |
---|
125 | if (!PListBuf) |
---|
126 | goto Fail; |
---|
127 | |
---|
128 | size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList); |
---|
129 | if (NumRead != (size_t)PListFileSize) |
---|
130 | goto Fail; |
---|
131 | |
---|
132 | /* Get the file buffer into CF's format. We pass in a null allocator here * |
---|
133 | * because we free PListBuf ourselves */ |
---|
134 | FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)( |
---|
135 | NULL, PListBuf, (CFIndex)NumRead, kCFAllocatorNull); |
---|
136 | if (!FileContentsRef) |
---|
137 | goto Fail; |
---|
138 | |
---|
139 | if (CFPropertyListCreateWithDataFunc) |
---|
140 | PListRef = (*CFPropertyListCreateWithDataFunc)( |
---|
141 | NULL, FileContentsRef, kCFPropertyListImmutable, NULL, NULL); |
---|
142 | else |
---|
143 | PListRef = (*CFPropertyListCreateFromXMLDataFunc)( |
---|
144 | NULL, FileContentsRef, kCFPropertyListImmutable, NULL); |
---|
145 | if (!PListRef) |
---|
146 | goto Fail; |
---|
147 | |
---|
148 | CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)( |
---|
149 | NULL, "ProductVersion", kCFStringEncodingASCII, kCFAllocatorNull); |
---|
150 | if (!ProductVersion) |
---|
151 | goto Fail; |
---|
152 | CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion); |
---|
153 | (*CFReleaseFunc)(ProductVersion); |
---|
154 | if (!OpaqueValue || |
---|
155 | (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)()) |
---|
156 | goto Fail; |
---|
157 | |
---|
158 | char VersionStr[32]; |
---|
159 | if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr, |
---|
160 | sizeof(VersionStr), kCFStringEncodingUTF8)) |
---|
161 | goto Fail; |
---|
162 | sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor); |
---|
163 | |
---|
164 | Fail: |
---|
165 | if (PListRef) |
---|
166 | (*CFReleaseFunc)(PListRef); |
---|
167 | if (FileContentsRef) |
---|
168 | (*CFReleaseFunc)(FileContentsRef); |
---|
169 | free(PListBuf); |
---|
170 | fclose(PropertyList); |
---|
171 | } |
---|
172 | |
---|
173 | int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) { |
---|
174 | /* Populate the global version variables, if they haven't already. */ |
---|
175 | #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 |
---|
176 | dispatch_once_f(&DispatchOnceCounter, NULL, parseSystemVersionPList); |
---|
177 | #else |
---|
178 | /* expensive procedure, only do once. GlobalMajor will not be 0 once run. */ |
---|
179 | if (GlobalMajor == 0) |
---|
180 | parseSystemVersionPList(NULL); |
---|
181 | #endif |
---|
182 | if (Major < GlobalMajor) return 1; |
---|
183 | if (Major > GlobalMajor) return 0; |
---|
184 | if (Minor < GlobalMinor) return 1; |
---|
185 | if (Minor > GlobalMinor) return 0; |
---|
186 | return Subminor <= GlobalSubminor; |
---|
187 | } |
---|
188 | |
---|
189 | #else |
---|
190 | |
---|
191 | /* Silence an empty translation unit warning. */ |
---|
192 | typedef int unused; |
---|
193 | |
---|
194 | #endif |
---|
195 | |
---|
196 | // Ken's Mini Test |
---|
197 | int main(void) { |
---|
198 | int32_t test; |
---|
199 | int32_t maj, min, sub; |
---|
200 | |
---|
201 | maj=10; sub=8; |
---|
202 | for (min=4; min < 10; min++) { |
---|
203 | for (sub=0; sub < 10; sub++) { |
---|
204 | printf("Testing Major %d, Minor %d, SubMinor %d\n", maj, min, sub); |
---|
205 | |
---|
206 | test = __isOSVersionAtLeast(maj,min,sub); |
---|
207 | |
---|
208 | printf("GlobalMajor %d, GlobalMinor %d, GlobalSubminor %d\n", GlobalMajor, |
---|
209 | GlobalMinor, GlobalSubminor); |
---|
210 | |
---|
211 | if (test > 0) { |
---|
212 | printf("We're the same or newer! %d \n\n", test); |
---|
213 | } |
---|
214 | if (test == 0) { |
---|
215 | printf("We're too old... %d \n\n", test); |
---|
216 | } |
---|
217 | } |
---|
218 | } |
---|
219 | |
---|
220 | return 0; |
---|
221 | } |
---|