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(OSKextVersionStage stage
)
175 case kOSKextVersionStageInvalid
: return NULL
;
176 case kOSKextVersionStageDevelopment
: return "d";
177 case kOSKextVersionStageAlpha
: return "a";
178 case kOSKextVersionStageBeta
: return "b";
179 case kOSKextVersionStageCandidate
: return "f";
180 case kOSKextVersionStageRelease
: return "";
184 /*********************************************************************
185 *********************************************************************/
187 OSKextParseVersionString(const char * versionString
)
189 OSKextVersion result
= -1;
191 int num_digits_scanned
= 0;
192 OSKextVersion vers_major
= 0;
193 OSKextVersion vers_minor
= 0;
194 OSKextVersion vers_revision
= 0;
195 OSKextVersion vers_stage
= 0;
196 OSKextVersion vers_stage_level
= 0;
197 const char * current_char_p
;
199 if (!versionString
|| *versionString
== '\0') {
203 current_char_p
= (const char *)&versionString
[0];
206 * Check for an initial digit of the major release number.
208 vers_major
= __vers_digit_for_char(*current_char_p
);
209 if (vers_major
< 0) {
214 num_digits_scanned
= 1;
216 /* Complete scan for major version number. Legal characters are
217 * any digit, period, any buildstage letter.
219 while (num_digits_scanned
< VERS_MAJOR_DIGITS
) {
220 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
221 vers_stage
= kOSKextVersionStageRelease
;
223 } else if (__vers_isdigit(*current_char_p
)) {
224 vers_digit
= __vers_digit_for_char(*current_char_p
);
225 if (vers_digit
< 0) {
228 vers_major
= (vers_major
) * 10 + vers_digit
;
230 num_digits_scanned
++;
231 } else if (__VERS_isreleasestate(*current_char_p
)) {
233 } else if (*current_char_p
== '.') {
241 /* Check for too many digits.
243 if (num_digits_scanned
== VERS_MAJOR_DIGITS
) {
244 if (*current_char_p
== '.') {
246 } else if (__vers_isdigit(*current_char_p
)) {
253 num_digits_scanned
= 0;
255 /* Scan for minor version number. Legal characters are
256 * any digit, period, any buildstage letter.
258 while (num_digits_scanned
< VERS_MINOR_DIGITS
) {
259 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
260 vers_stage
= kOSKextVersionStageRelease
;
262 } else if (__vers_isdigit(*current_char_p
)) {
263 vers_digit
= __vers_digit_for_char(*current_char_p
);
264 if (vers_digit
< 0) {
267 vers_minor
= (vers_minor
) * 10 + vers_digit
;
269 num_digits_scanned
++;
270 } else if (__VERS_isreleasestate(*current_char_p
)) {
272 } else if (*current_char_p
== '.') {
280 /* Check for too many digits.
282 if (num_digits_scanned
== VERS_MINOR_DIGITS
) {
283 if (*current_char_p
== '.') {
285 } else if (__vers_isdigit(*current_char_p
)) {
292 num_digits_scanned
= 0;
294 /* Scan for revision version number. Legal characters are
295 * any digit, any buildstage letter (NOT PERIOD).
297 while (num_digits_scanned
< VERS_REVISION_DIGITS
) {
298 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
299 vers_stage
= kOSKextVersionStageRelease
;
301 } else if (__vers_isdigit(*current_char_p
)) {
302 vers_digit
= __vers_digit_for_char(*current_char_p
);
303 if (vers_digit
< 0) {
306 vers_revision
= (vers_revision
) * 10 + vers_digit
;
308 num_digits_scanned
++;
309 } else if (__VERS_isreleasestate(*current_char_p
)) {
316 /* Check for too many digits.
318 if (num_digits_scanned
== VERS_REVISION_DIGITS
) {
319 if (*current_char_p
== '.') {
321 } else if (__vers_isdigit(*current_char_p
)) {
329 * Check for the release state.
331 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
332 vers_stage
= kOSKextVersionStageRelease
;
335 vers_stage
= __OSKextVersionStageForString(¤t_char_p
);
336 if (vers_stage
== kOSKextVersionStageInvalid
) {
344 num_digits_scanned
= 0;
346 /* Scan for stage level number. Legal characters are
349 while (num_digits_scanned
< VERS_STAGE_LEVEL_DIGITS
) {
350 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
351 if (num_digits_scanned
) {
356 } else if (__vers_isdigit(*current_char_p
)) {
357 vers_digit
= __vers_digit_for_char(*current_char_p
);
358 if (vers_digit
< 0) {
361 vers_stage_level
= (vers_stage_level
) * 10 + vers_digit
;
363 num_digits_scanned
++;
369 /* Check for too many digits.
371 if ((num_digits_scanned
== VERS_STAGE_LEVEL_DIGITS
) &&
372 !(__vers_isspace(*current_char_p
) || (*current_char_p
== '\0'))) {
376 if (vers_stage_level
> VERS_STAGE_LEVEL_MAX
) {
382 if (vers_stage
== kOSKextVersionStageCandidate
&& vers_stage_level
== 0) {
386 result
= (vers_major
* VERS_MAJOR_MULT
) +
387 (vers_minor
* VERS_MINOR_MULT
) +
388 (vers_revision
* VERS_REVISION_MULT
) +
389 (vers_stage
* VERS_STAGE_MULT
) +
395 /*********************************************************************
396 * This function must be safe to call in panic context.
397 *********************************************************************/
399 OSKextVersionGetString(
400 OSKextVersion aVersion
,
402 uint32_t bufferLength
)
405 OSKextVersion vers_major
= 0;
406 OSKextVersion vers_minor
= 0;
407 OSKextVersion vers_revision
= 0;
408 OSKextVersion vers_stage
= 0;
409 OSKextVersion vers_stage_level
= 0;
410 const char * stage_string
= NULL
;// don't free
412 /* No buffer or length less than longest possible vers string,
415 if (!buffer
|| bufferLength
< kOSKextVersionMaxLength
) {
419 bzero(buffer
, bufferLength
* sizeof(char));
422 strlcpy(buffer
, "(invalid)", bufferLength
);
426 strlcpy(buffer
, "(missing)", bufferLength
);
430 vers_major
= aVersion
/ VERS_MAJOR_MULT
;
431 if (vers_major
> VERS_MAJOR_MAX
) {
432 strlcpy(buffer
, "(invalid)", bufferLength
);
436 vers_minor
= aVersion
- (vers_major
* VERS_MAJOR_MULT
);
437 vers_minor
/= VERS_MINOR_MULT
;
439 vers_revision
= aVersion
-
440 ((vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
));
441 vers_revision
/= VERS_REVISION_MULT
;
443 vers_stage
= aVersion
-
444 ((vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
) +
445 (vers_revision
* VERS_REVISION_MULT
));
446 vers_stage
/= VERS_STAGE_MULT
;
448 vers_stage_level
= aVersion
-
449 ((vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
) +
450 (vers_revision
* VERS_REVISION_MULT
) + (vers_stage
* VERS_STAGE_MULT
));
451 if (vers_stage_level
> VERS_STAGE_LEVEL_MAX
) {
452 strlcpy(buffer
, "(invalid)", bufferLength
);
456 cpos
= scnprintf(buffer
, bufferLength
, "%u", (uint32_t)vers_major
);
458 /* Always include the minor version; it just looks weird without.
462 cpos
+= scnprintf(buffer
+ cpos
, bufferLength
- cpos
, "%u", (uint32_t)vers_minor
);
464 /* The revision is displayed only if nonzero.
469 cpos
+= scnprintf(buffer
+ cpos
, bufferLength
- cpos
, "%u",
470 (uint32_t)vers_revision
);
473 stage_string
= __OSKextVersionStringForStage(vers_stage
);
475 strlcpy(buffer
, "(invalid)", bufferLength
);
478 if (stage_string
[0]) {
479 strlcat(buffer
, stage_string
, bufferLength
);
480 cpos
+= strlen(stage_string
);
483 if (vers_stage
< kOSKextVersionStageRelease
) {
484 snprintf(buffer
+ cpos
, bufferLength
- cpos
, "%u", (uint32_t)vers_stage_level
);
490 /*********************************************************************
491 *********************************************************************/
494 OSKextParseVersionCFString(CFStringRef versionString
)
496 OSKextVersion result
= -1;
497 char versBuffer
[kOSKextVersionMaxLength
];
499 if (CFStringGetCString(versionString
, versBuffer
,
500 sizeof(versBuffer
), kCFStringEncodingASCII
)) {
501 result
= OSKextParseVersionString(versBuffer
);