]> git.saurik.com Git - apple/xnu.git/blob - libkern/OSKextVersion.c
xnu-1504.9.17.tar.gz
[apple/xnu.git] / libkern / OSKextVersion.c
1 /*
2 * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 #ifdef KERNEL
29 #include <sys/systm.h>
30 #include <libkern/OSKextLib.h>
31 #include <libkern/OSKextLibPrivate.h>
32 #else
33 #include <libc.h>
34 #include <System/libkern/OSKextLib.h>
35 #include <System/libkern/OSKextLibPrivate.h>
36 #endif /* KERNEL */
37
38 #include <libkern/OSKextLibPrivate.h>
39
40 #define VERS_MAJOR_DIGITS (4)
41 #define VERS_MINOR_DIGITS (2)
42 #define VERS_REVISION_DIGITS (2)
43 #define VERS_STAGE_DIGITS (1)
44 #define VERS_STAGE_LEVEL_DIGITS (3)
45
46 #define VERS_MAJOR_MAX (9999)
47 #define VERS_STAGE_LEVEL_MAX (255)
48
49 #define VERS_MAJOR_MULT (100000000)
50 #define VERS_MINOR_MULT (1000000)
51 #define VERS_REVISION_MULT (10000)
52 #define VERS_STAGE_MULT (1000)
53
54
55 typedef enum {
56 kOSKextVersionStageInvalid = 0,
57 kOSKextVersionStageDevelopment = 1,
58 kOSKextVersionStageAlpha = 3,
59 kOSKextVersionStageBeta = 5,
60 kOSKextVersionStageCandidate = 7,
61 kOSKextVersionStageRelease = 9,
62 } OSKextVersionStage;
63
64
65 /*********************************************************************
66 *********************************************************************/
67 static int __vers_isdigit(char c) {
68 return (c == '0' ||
69 c == '1' || c == '2' || c == '3' ||
70 c == '4' || c == '5' || c == '6' ||
71 c == '7' || c == '8' || c == '9');
72 }
73
74 /*********************************************************************
75 *********************************************************************/
76 static int __vers_isspace(char c) {
77 return (c == ' ' ||
78 c == '\t' ||
79 c == '\r' ||
80 c == '\n');
81 }
82
83 /*********************************************************************
84 *********************************************************************/
85 static int __vers_digit_for_char(char c) {
86 switch (c) {
87 case '0': return 0; break;
88 case '1': return 1; break;
89 case '2': return 2; break;
90 case '3': return 3; break;
91 case '4': return 4; break;
92 case '5': return 5; break;
93 case '6': return 6; break;
94 case '7': return 7; break;
95 case '8': return 8; break;
96 case '9': return 9; break;
97 default: return -1; break;
98 }
99
100 return -1;
101 }
102
103 /*********************************************************************
104 *********************************************************************/
105 static int __VERS_isreleasestate(char c) {
106 return (c == 'd' || c == 'a' || c == 'b' || c == 'f');
107 }
108
109
110 /*********************************************************************
111 *********************************************************************/
112 static OSKextVersionStage __OSKextVersionStageForString(const char ** string_p) {
113 const char * string;
114
115 if (!string_p || !*string_p) {
116 return kOSKextVersionStageInvalid;
117 }
118
119 string = *string_p;
120
121 if (__vers_isspace(string[0]) || string[0] == '\0') {
122 return kOSKextVersionStageRelease;
123 } else {
124 switch (string[0]) {
125 case 'd':
126 if (__vers_isdigit(string[1])) {
127 *string_p = &string[1];
128 return kOSKextVersionStageDevelopment;
129 }
130 break;
131 case 'a':
132 if (__vers_isdigit(string[1])) {
133 *string_p = &string[1];
134 return kOSKextVersionStageAlpha;
135 }
136 break;
137 case 'b':
138 if (__vers_isdigit(string[1])) {
139 *string_p = &string[1];
140 return kOSKextVersionStageBeta;
141 }
142 break;
143 case 'f':
144 if (__vers_isdigit(string[1])) {
145 *string_p = &string[1];
146 return kOSKextVersionStageCandidate;
147 } else if (string[1] == 'c' && __vers_isdigit(string[2])) {
148 *string_p = &string[2];
149 return kOSKextVersionStageCandidate;
150 } else {
151 return kOSKextVersionStageInvalid;
152 }
153 break;
154 default:
155 return kOSKextVersionStageInvalid;
156 break;
157 }
158 }
159
160 return kOSKextVersionStageInvalid;
161 }
162
163 /*********************************************************************
164 *********************************************************************/
165 static const char * __OSKextVersionStringForStage(OSKextVersionStage stage)
166 {
167 switch (stage) {
168 case kOSKextVersionStageInvalid: return NULL; break;
169 case kOSKextVersionStageDevelopment: return "d"; break;
170 case kOSKextVersionStageAlpha: return "a"; break;
171 case kOSKextVersionStageBeta: return "b"; break;
172 case kOSKextVersionStageCandidate: return "f"; break;
173 case kOSKextVersionStageRelease: return ""; break;
174 }
175
176 return NULL;
177 }
178
179 /*********************************************************************
180 *********************************************************************/
181 OSKextVersion OSKextParseVersionString(const char * versionString)
182 {
183 OSKextVersion result = -1;
184 int vers_digit = -1;
185 int num_digits_scanned = 0;
186 OSKextVersion vers_major = 0;
187 OSKextVersion vers_minor = 0;
188 OSKextVersion vers_revision = 0;
189 OSKextVersion vers_stage = 0;
190 OSKextVersion vers_stage_level = 0;
191 const char * current_char_p;
192
193 if (!versionString || *versionString == '\0') {
194 return -1;
195 }
196
197 current_char_p = (const char *)&versionString[0];
198
199 /*****
200 * Check for an initial digit of the major release number.
201 */
202 vers_major = __vers_digit_for_char(*current_char_p);
203 if (vers_major < 0) {
204 return -1;
205 }
206
207 current_char_p++;
208 num_digits_scanned = 1;
209
210 /* Complete scan for major version number. Legal characters are
211 * any digit, period, any buildstage letter.
212 */
213 while (num_digits_scanned < VERS_MAJOR_DIGITS) {
214 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
215 vers_stage = kOSKextVersionStageRelease;
216 goto finish;
217 } else if (__vers_isdigit(*current_char_p)) {
218 vers_digit = __vers_digit_for_char(*current_char_p);
219 if (vers_digit < 0) {
220 return -1;
221 }
222 vers_major = (vers_major) * 10 + vers_digit;
223 current_char_p++;
224 num_digits_scanned++;
225 } else if (__VERS_isreleasestate(*current_char_p)) {
226 goto release_state;
227 } else if (*current_char_p == '.') {
228 current_char_p++;
229 goto minor_version;
230 } else {
231 return -1;
232 }
233 }
234
235 /* Check for too many digits.
236 */
237 if (num_digits_scanned == VERS_MAJOR_DIGITS) {
238 if (*current_char_p == '.') {
239 current_char_p++;
240 } else if (__vers_isdigit(*current_char_p)) {
241 return -1;
242 }
243 }
244
245 minor_version:
246
247 num_digits_scanned = 0;
248
249 /* Scan for minor version number. Legal characters are
250 * any digit, period, any buildstage letter.
251 */
252 while (num_digits_scanned < VERS_MINOR_DIGITS) {
253 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
254 vers_stage = kOSKextVersionStageRelease;
255 goto finish;
256 } else if (__vers_isdigit(*current_char_p)) {
257 vers_digit = __vers_digit_for_char(*current_char_p);
258 if (vers_digit < 0) {
259 return -1;
260 }
261 vers_minor = (vers_minor) * 10 + vers_digit;
262 current_char_p++;
263 num_digits_scanned++;
264 } else if (__VERS_isreleasestate(*current_char_p)) {
265 goto release_state;
266 } else if (*current_char_p == '.') {
267 current_char_p++;
268 goto revision;
269 } else {
270 return -1;
271 }
272 }
273
274 /* Check for too many digits.
275 */
276 if (num_digits_scanned == VERS_MINOR_DIGITS) {
277 if (*current_char_p == '.') {
278 current_char_p++;
279 } else if (__vers_isdigit(*current_char_p)) {
280 return -1;
281 }
282 }
283
284 revision:
285
286 num_digits_scanned = 0;
287
288 /* Scan for revision version number. Legal characters are
289 * any digit, any buildstage letter (NOT PERIOD).
290 */
291 while (num_digits_scanned < VERS_REVISION_DIGITS) {
292 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
293 vers_stage = kOSKextVersionStageRelease;
294 goto finish;
295 } else if (__vers_isdigit(*current_char_p)) {
296 vers_digit = __vers_digit_for_char(*current_char_p);
297 if (vers_digit < 0) {
298 return -1;
299 }
300 vers_revision = (vers_revision) * 10 + vers_digit;
301 current_char_p++;
302 num_digits_scanned++;
303 } else if (__VERS_isreleasestate(*current_char_p)) {
304 goto release_state;
305 } else {
306 return -1;
307 }
308 }
309
310 /* Check for too many digits.
311 */
312 if (num_digits_scanned == VERS_REVISION_DIGITS) {
313 if (*current_char_p == '.') {
314 current_char_p++;
315 } else if (__vers_isdigit(*current_char_p)) {
316 return -1;
317 }
318 }
319
320 release_state:
321
322 /*****
323 * Check for the release state.
324 */
325 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
326 vers_stage = kOSKextVersionStageRelease;
327 goto finish;
328 } else {
329 vers_stage = __OSKextVersionStageForString(&current_char_p);
330 if (vers_stage == kOSKextVersionStageInvalid) {
331 return -1;
332 }
333 }
334
335
336 // stage level
337
338 num_digits_scanned = 0;
339
340 /* Scan for stage level number. Legal characters are
341 * any digit only.
342 */
343 while (num_digits_scanned < VERS_STAGE_LEVEL_DIGITS) {
344 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
345 if (num_digits_scanned) {
346 goto finish;
347 } else {
348 return -1;
349 }
350 } else if (__vers_isdigit(*current_char_p)) {
351 vers_digit = __vers_digit_for_char(*current_char_p);
352 if (vers_digit < 0) {
353 return -1;
354 }
355 vers_stage_level = (vers_stage_level) * 10 + vers_digit;
356 current_char_p++;
357 num_digits_scanned++;
358 } else {
359 return -1;
360 }
361 }
362
363 /* Check for too many digits.
364 */
365 if ((num_digits_scanned == VERS_STAGE_LEVEL_DIGITS) &&
366 ! (__vers_isspace(*current_char_p) || (*current_char_p == '\0'))) {
367
368 return -1;
369 }
370
371 if (vers_stage_level > VERS_STAGE_LEVEL_MAX) {
372 return -1;
373 }
374
375 finish:
376
377 if (vers_stage == kOSKextVersionStageCandidate && vers_stage_level == 0) {
378 return -1;
379 }
380
381 result = (vers_major * VERS_MAJOR_MULT) +
382 (vers_minor * VERS_MINOR_MULT) +
383 (vers_revision * VERS_REVISION_MULT) +
384 (vers_stage * VERS_STAGE_MULT) +
385 vers_stage_level;
386
387 return result;
388 }
389
390 /*********************************************************************
391 *********************************************************************/
392 Boolean OSKextVersionGetString(
393 OSKextVersion aVersion,
394 char * buffer,
395 uint32_t bufferLength)
396 {
397 int cpos = 0;
398 OSKextVersion vers_major = 0;
399 OSKextVersion vers_minor = 0;
400 OSKextVersion vers_revision = 0;
401 OSKextVersion vers_stage = 0;
402 OSKextVersion vers_stage_level = 0;
403 const char * stage_string = NULL; // don't free
404
405 /* No buffer or length less than longest possible vers string,
406 * return 0.
407 */
408 if (!buffer || bufferLength < kOSKextVersionMaxLength) {
409 return FALSE;
410 }
411
412 bzero(buffer, bufferLength * sizeof(char));
413
414 if (aVersion < 0) {
415 strlcpy(buffer, "(invalid)", bufferLength);
416 return TRUE;
417 }
418 if (aVersion == 0) {
419 strlcpy(buffer, "(missing)", bufferLength);
420 return TRUE;
421 }
422
423 vers_major = aVersion / VERS_MAJOR_MULT;
424 if (vers_major > VERS_MAJOR_MAX) {
425 strlcpy(buffer, "(invalid)", bufferLength);
426 return TRUE;
427 }
428
429 vers_minor = aVersion - (vers_major * VERS_MAJOR_MULT);
430 vers_minor /= VERS_MINOR_MULT;
431
432 vers_revision = aVersion -
433 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) );
434 vers_revision /= VERS_REVISION_MULT;
435
436 vers_stage = aVersion -
437 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
438 (vers_revision * VERS_REVISION_MULT));
439 vers_stage /= VERS_STAGE_MULT;
440
441 vers_stage_level = aVersion -
442 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
443 (vers_revision * VERS_REVISION_MULT) + (vers_stage * VERS_STAGE_MULT));
444 if (vers_stage_level > VERS_STAGE_LEVEL_MAX) {
445 strlcpy(buffer, "(invalid)", bufferLength);
446 return TRUE;
447 }
448
449 cpos = snprintf(buffer, bufferLength, "%u", (uint32_t)vers_major);
450
451 /* Always include the minor version; it just looks weird without.
452 */
453 buffer[cpos] = '.';
454 cpos++;
455 cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_minor);
456
457 /* The revision is displayed only if nonzero.
458 */
459 if (vers_revision) {
460 buffer[cpos] = '.';
461 cpos++;
462 cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u",
463 (uint32_t)vers_revision);
464 }
465
466 stage_string = __OSKextVersionStringForStage(vers_stage);
467 if (!stage_string) {
468 strlcpy(buffer, "(invalid)", bufferLength);
469 return TRUE;
470 }
471 if (stage_string[0]) {
472 strlcat(buffer, stage_string, bufferLength);
473 cpos += strlen(stage_string);
474 }
475
476 if (vers_stage < kOSKextVersionStageRelease) {
477 snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_stage_level);
478 }
479
480 return TRUE;
481 }
482
483 /*********************************************************************
484 *********************************************************************/
485 #ifndef KERNEL
486 OSKextVersion OSKextParseVersionCFString(CFStringRef versionString)
487 {
488 OSKextVersion result = -1;
489 char versBuffer[kOSKextVersionMaxLength];
490
491 if (CFStringGetCString(versionString, versBuffer,
492 sizeof(versBuffer), kCFStringEncodingASCII)) {
493
494 result = OSKextParseVersionString(versBuffer);
495 }
496 return result;
497 }
498 #endif