2 * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/systm.h>
30 #include <libkern/OSKextLib.h>
31 #include <libkern/OSKextLibPrivate.h>
34 #include <libkern/OSKextLib.h>
35 #include <System/libkern/OSKextLibPrivate.h>
38 #include <libkern/OSKextLibPrivate.h>
40 #define VERS_MAJOR_DIGITS (4)
41 #define VERS_MINOR_DIGITS (4)
42 #define VERS_REVISION_DIGITS (4)
43 #define VERS_STAGE_DIGITS (1)
44 #define VERS_STAGE_LEVEL_DIGITS (3)
46 #define VERS_MAJOR_MAX (9999)
47 #define VERS_STAGE_LEVEL_MAX (255)
49 #define VERS_MAJOR_MULT (1000000000000)
50 #define VERS_MINOR_MULT (100000000)
51 #define VERS_REVISION_MULT (10000)
52 #define VERS_STAGE_MULT (1000)
56 kOSKextVersionStageInvalid
= 0,
57 kOSKextVersionStageDevelopment
= 1,
58 kOSKextVersionStageAlpha
= 3,
59 kOSKextVersionStageBeta
= 5,
60 kOSKextVersionStageCandidate
= 7,
61 kOSKextVersionStageRelease
= 9,
65 /*********************************************************************
66 *********************************************************************/
68 __vers_isdigit(char c
)
71 c
== '1' || c
== '2' || c
== '3' ||
72 c
== '4' || c
== '5' || c
== '6' ||
73 c
== '7' || c
== '8' || c
== '9';
76 /*********************************************************************
77 *********************************************************************/
79 __vers_isspace(char c
)
87 /*********************************************************************
88 *********************************************************************/
90 __vers_digit_for_char(char c
)
107 /*********************************************************************
108 *********************************************************************/
110 __VERS_isreleasestate(char c
)
112 return c
== 'd' || c
== 'a' || c
== 'b' || c
== 'f';
116 /*********************************************************************
117 *********************************************************************/
118 static OSKextVersionStage
119 __OSKextVersionStageForString(const char ** string_p
)
123 if (!string_p
|| !*string_p
) {
124 return kOSKextVersionStageInvalid
;
129 if (__vers_isspace(string
[0]) || string
[0] == '\0') {
130 return kOSKextVersionStageRelease
;
134 if (__vers_isdigit(string
[1])) {
135 *string_p
= &string
[1];
136 return kOSKextVersionStageDevelopment
;
140 if (__vers_isdigit(string
[1])) {
141 *string_p
= &string
[1];
142 return kOSKextVersionStageAlpha
;
146 if (__vers_isdigit(string
[1])) {
147 *string_p
= &string
[1];
148 return kOSKextVersionStageBeta
;
152 if (__vers_isdigit(string
[1])) {
153 *string_p
= &string
[1];
154 return kOSKextVersionStageCandidate
;
155 } else if (string
[1] == 'c' && __vers_isdigit(string
[2])) {
156 *string_p
= &string
[2];
157 return kOSKextVersionStageCandidate
;
159 return kOSKextVersionStageInvalid
;
162 return kOSKextVersionStageInvalid
;
166 return kOSKextVersionStageInvalid
;
169 /*********************************************************************
170 *********************************************************************/
172 __OSKextVersionStringForStage(OSKextVersion stage
)
177 case kOSKextVersionStageInvalid
: return NULL
;
178 case kOSKextVersionStageDevelopment
: return "d";
179 case kOSKextVersionStageAlpha
: return "a";
180 case kOSKextVersionStageBeta
: return "b";
181 case kOSKextVersionStageCandidate
: return "f";
182 case kOSKextVersionStageRelease
: return "";
186 /*********************************************************************
187 *********************************************************************/
189 OSKextParseVersionString(const char * versionString
)
191 OSKextVersion result
= -1;
193 int num_digits_scanned
= 0;
194 OSKextVersion vers_major
= 0;
195 OSKextVersion vers_minor
= 0;
196 OSKextVersion vers_revision
= 0;
197 OSKextVersionStage vers_stage
= 0;
198 OSKextVersion vers_stage_level
= 0;
199 const char * current_char_p
;
201 if (!versionString
|| *versionString
== '\0') {
205 current_char_p
= (const char *)&versionString
[0];
208 * Check for an initial digit of the major release number.
210 vers_major
= __vers_digit_for_char(*current_char_p
);
211 if (vers_major
< 0) {
216 num_digits_scanned
= 1;
218 /* Complete scan for major version number. Legal characters are
219 * any digit, period, any buildstage letter.
221 while (num_digits_scanned
< VERS_MAJOR_DIGITS
) {
222 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
223 vers_stage
= kOSKextVersionStageRelease
;
225 } else if (__vers_isdigit(*current_char_p
)) {
226 vers_digit
= __vers_digit_for_char(*current_char_p
);
227 if (vers_digit
< 0) {
230 vers_major
= (vers_major
) * 10 + vers_digit
;
232 num_digits_scanned
++;
233 } else if (__VERS_isreleasestate(*current_char_p
)) {
235 } else if (*current_char_p
== '.') {
243 /* Check for too many digits.
245 if (num_digits_scanned
== VERS_MAJOR_DIGITS
) {
246 if (*current_char_p
== '.') {
248 } else if (__vers_isdigit(*current_char_p
)) {
255 num_digits_scanned
= 0;
257 /* Scan for minor version number. Legal characters are
258 * any digit, period, any buildstage letter.
260 while (num_digits_scanned
< VERS_MINOR_DIGITS
) {
261 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
262 vers_stage
= kOSKextVersionStageRelease
;
264 } else if (__vers_isdigit(*current_char_p
)) {
265 vers_digit
= __vers_digit_for_char(*current_char_p
);
266 if (vers_digit
< 0) {
269 vers_minor
= (vers_minor
) * 10 + vers_digit
;
271 num_digits_scanned
++;
272 } else if (__VERS_isreleasestate(*current_char_p
)) {
274 } else if (*current_char_p
== '.') {
282 /* Check for too many digits.
284 if (num_digits_scanned
== VERS_MINOR_DIGITS
) {
285 if (*current_char_p
== '.') {
287 } else if (__vers_isdigit(*current_char_p
)) {
294 num_digits_scanned
= 0;
296 /* Scan for revision version number. Legal characters are
297 * any digit, any buildstage letter (NOT PERIOD).
299 while (num_digits_scanned
< VERS_REVISION_DIGITS
) {
300 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
301 vers_stage
= kOSKextVersionStageRelease
;
303 } else if (__vers_isdigit(*current_char_p
)) {
304 vers_digit
= __vers_digit_for_char(*current_char_p
);
305 if (vers_digit
< 0) {
308 vers_revision
= (vers_revision
) * 10 + vers_digit
;
310 num_digits_scanned
++;
311 } else if (__VERS_isreleasestate(*current_char_p
)) {
318 /* Check for too many digits.
320 if (num_digits_scanned
== VERS_REVISION_DIGITS
) {
321 if (*current_char_p
== '.') {
323 } else if (__vers_isdigit(*current_char_p
)) {
331 * Check for the release state.
333 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
334 vers_stage
= kOSKextVersionStageRelease
;
337 vers_stage
= __OSKextVersionStageForString(¤t_char_p
);
338 if (vers_stage
== kOSKextVersionStageInvalid
) {
346 num_digits_scanned
= 0;
348 /* Scan for stage level number. Legal characters are
351 while (num_digits_scanned
< VERS_STAGE_LEVEL_DIGITS
) {
352 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
353 if (num_digits_scanned
) {
358 } else if (__vers_isdigit(*current_char_p
)) {
359 vers_digit
= __vers_digit_for_char(*current_char_p
);
360 if (vers_digit
< 0) {
363 vers_stage_level
= (vers_stage_level
) * 10 + vers_digit
;
365 num_digits_scanned
++;
371 /* Check for too many digits.
373 if ((num_digits_scanned
== VERS_STAGE_LEVEL_DIGITS
) &&
374 !(__vers_isspace(*current_char_p
) || (*current_char_p
== '\0'))) {
378 if (vers_stage_level
> VERS_STAGE_LEVEL_MAX
) {
384 if (vers_stage
== kOSKextVersionStageCandidate
&& vers_stage_level
== 0) {
388 result
= (vers_major
* VERS_MAJOR_MULT
) +
389 (vers_minor
* VERS_MINOR_MULT
) +
390 (vers_revision
* VERS_REVISION_MULT
) +
391 (vers_stage
* VERS_STAGE_MULT
) +
397 /*********************************************************************
398 * This function must be safe to call in panic context.
399 *********************************************************************/
401 OSKextVersionGetString(
402 OSKextVersion aVersion
,
404 uint32_t bufferLength
)
407 OSKextVersion vers_major
= 0;
408 OSKextVersion vers_minor
= 0;
409 OSKextVersion vers_revision
= 0;
410 OSKextVersion vers_stage
= 0;
411 OSKextVersion vers_stage_level
= 0;
412 const char * stage_string
= NULL
;// don't free
414 /* No buffer or length less than longest possible vers string,
417 if (!buffer
|| bufferLength
< kOSKextVersionMaxLength
) {
421 bzero(buffer
, bufferLength
* sizeof(char));
424 strlcpy(buffer
, "(invalid)", bufferLength
);
428 strlcpy(buffer
, "(missing)", bufferLength
);
432 vers_major
= aVersion
/ VERS_MAJOR_MULT
;
433 if (vers_major
> VERS_MAJOR_MAX
) {
434 strlcpy(buffer
, "(invalid)", bufferLength
);
438 vers_minor
= aVersion
- (vers_major
* VERS_MAJOR_MULT
);
439 vers_minor
/= VERS_MINOR_MULT
;
441 vers_revision
= aVersion
-
442 ((vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
));
443 vers_revision
/= VERS_REVISION_MULT
;
445 vers_stage
= aVersion
-
446 ((vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
) +
447 (vers_revision
* VERS_REVISION_MULT
));
448 vers_stage
/= VERS_STAGE_MULT
;
450 vers_stage_level
= aVersion
-
451 ((vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
) +
452 (vers_revision
* VERS_REVISION_MULT
) + (vers_stage
* VERS_STAGE_MULT
));
453 if (vers_stage_level
> VERS_STAGE_LEVEL_MAX
) {
454 strlcpy(buffer
, "(invalid)", bufferLength
);
458 cpos
= scnprintf(buffer
, bufferLength
, "%u", (uint32_t)vers_major
);
460 /* Always include the minor version; it just looks weird without.
464 cpos
+= scnprintf(buffer
+ cpos
, bufferLength
- cpos
, "%u", (uint32_t)vers_minor
);
466 /* The revision is displayed only if nonzero.
471 cpos
+= scnprintf(buffer
+ cpos
, bufferLength
- cpos
, "%u",
472 (uint32_t)vers_revision
);
475 stage_string
= __OSKextVersionStringForStage(vers_stage
);
477 strlcpy(buffer
, "(invalid)", bufferLength
);
480 if (stage_string
[0]) {
481 strlcat(buffer
, stage_string
, bufferLength
);
482 cpos
+= strlen(stage_string
);
485 if (vers_stage
< kOSKextVersionStageRelease
) {
486 snprintf(buffer
+ cpos
, bufferLength
- cpos
, "%u", (uint32_t)vers_stage_level
);
492 /*********************************************************************
493 *********************************************************************/
496 OSKextParseVersionCFString(CFStringRef versionString
)
498 OSKextVersion result
= -1;
499 char versBuffer
[kOSKextVersionMaxLength
];
501 if (CFStringGetCString(versionString
, versBuffer
,
502 sizeof(versBuffer
), kCFStringEncodingASCII
)) {
503 result
= OSKextParseVersionString(versBuffer
);