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 *********************************************************************/
67 static int __vers_isdigit(char c
) {
69 c
== '1' || c
== '2' || c
== '3' ||
70 c
== '4' || c
== '5' || c
== '6' ||
71 c
== '7' || c
== '8' || c
== '9');
74 /*********************************************************************
75 *********************************************************************/
76 static int __vers_isspace(char c
) {
83 /*********************************************************************
84 *********************************************************************/
86 __vers_digit_for_char(char c
) {
102 /*********************************************************************
103 *********************************************************************/
104 static int __VERS_isreleasestate(char c
) {
105 return (c
== 'd' || c
== 'a' || c
== 'b' || c
== 'f');
109 /*********************************************************************
110 *********************************************************************/
111 static OSKextVersionStage
__OSKextVersionStageForString(const char ** string_p
) {
114 if (!string_p
|| !*string_p
) {
115 return kOSKextVersionStageInvalid
;
120 if (__vers_isspace(string
[0]) || string
[0] == '\0') {
121 return kOSKextVersionStageRelease
;
125 if (__vers_isdigit(string
[1])) {
126 *string_p
= &string
[1];
127 return kOSKextVersionStageDevelopment
;
131 if (__vers_isdigit(string
[1])) {
132 *string_p
= &string
[1];
133 return kOSKextVersionStageAlpha
;
137 if (__vers_isdigit(string
[1])) {
138 *string_p
= &string
[1];
139 return kOSKextVersionStageBeta
;
143 if (__vers_isdigit(string
[1])) {
144 *string_p
= &string
[1];
145 return kOSKextVersionStageCandidate
;
146 } else if (string
[1] == 'c' && __vers_isdigit(string
[2])) {
147 *string_p
= &string
[2];
148 return kOSKextVersionStageCandidate
;
150 return kOSKextVersionStageInvalid
;
153 return kOSKextVersionStageInvalid
;
157 return kOSKextVersionStageInvalid
;
160 /*********************************************************************
161 *********************************************************************/
163 __OSKextVersionStringForStage(OSKextVersionStage stage
)
166 case kOSKextVersionStageInvalid
: return NULL
;
167 case kOSKextVersionStageDevelopment
: return "d";
168 case kOSKextVersionStageAlpha
: return "a";
169 case kOSKextVersionStageBeta
: return "b";
170 case kOSKextVersionStageCandidate
: return "f";
171 case kOSKextVersionStageRelease
: return "";
175 /*********************************************************************
176 *********************************************************************/
177 OSKextVersion
OSKextParseVersionString(const char * versionString
)
179 OSKextVersion result
= -1;
181 int num_digits_scanned
= 0;
182 OSKextVersion vers_major
= 0;
183 OSKextVersion vers_minor
= 0;
184 OSKextVersion vers_revision
= 0;
185 OSKextVersion vers_stage
= 0;
186 OSKextVersion vers_stage_level
= 0;
187 const char * current_char_p
;
189 if (!versionString
|| *versionString
== '\0') {
193 current_char_p
= (const char *)&versionString
[0];
196 * Check for an initial digit of the major release number.
198 vers_major
= __vers_digit_for_char(*current_char_p
);
199 if (vers_major
< 0) {
204 num_digits_scanned
= 1;
206 /* Complete scan for major version number. Legal characters are
207 * any digit, period, any buildstage letter.
209 while (num_digits_scanned
< VERS_MAJOR_DIGITS
) {
210 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
211 vers_stage
= kOSKextVersionStageRelease
;
213 } else if (__vers_isdigit(*current_char_p
)) {
214 vers_digit
= __vers_digit_for_char(*current_char_p
);
215 if (vers_digit
< 0) {
218 vers_major
= (vers_major
) * 10 + vers_digit
;
220 num_digits_scanned
++;
221 } else if (__VERS_isreleasestate(*current_char_p
)) {
223 } else if (*current_char_p
== '.') {
231 /* Check for too many digits.
233 if (num_digits_scanned
== VERS_MAJOR_DIGITS
) {
234 if (*current_char_p
== '.') {
236 } else if (__vers_isdigit(*current_char_p
)) {
243 num_digits_scanned
= 0;
245 /* Scan for minor version number. Legal characters are
246 * any digit, period, any buildstage letter.
248 while (num_digits_scanned
< VERS_MINOR_DIGITS
) {
249 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
250 vers_stage
= kOSKextVersionStageRelease
;
252 } else if (__vers_isdigit(*current_char_p
)) {
253 vers_digit
= __vers_digit_for_char(*current_char_p
);
254 if (vers_digit
< 0) {
257 vers_minor
= (vers_minor
) * 10 + vers_digit
;
259 num_digits_scanned
++;
260 } else if (__VERS_isreleasestate(*current_char_p
)) {
262 } else if (*current_char_p
== '.') {
270 /* Check for too many digits.
272 if (num_digits_scanned
== VERS_MINOR_DIGITS
) {
273 if (*current_char_p
== '.') {
275 } else if (__vers_isdigit(*current_char_p
)) {
282 num_digits_scanned
= 0;
284 /* Scan for revision version number. Legal characters are
285 * any digit, any buildstage letter (NOT PERIOD).
287 while (num_digits_scanned
< VERS_REVISION_DIGITS
) {
288 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
289 vers_stage
= kOSKextVersionStageRelease
;
291 } else if (__vers_isdigit(*current_char_p
)) {
292 vers_digit
= __vers_digit_for_char(*current_char_p
);
293 if (vers_digit
< 0) {
296 vers_revision
= (vers_revision
) * 10 + vers_digit
;
298 num_digits_scanned
++;
299 } else if (__VERS_isreleasestate(*current_char_p
)) {
306 /* Check for too many digits.
308 if (num_digits_scanned
== VERS_REVISION_DIGITS
) {
309 if (*current_char_p
== '.') {
311 } else if (__vers_isdigit(*current_char_p
)) {
319 * Check for the release state.
321 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
322 vers_stage
= kOSKextVersionStageRelease
;
325 vers_stage
= __OSKextVersionStageForString(¤t_char_p
);
326 if (vers_stage
== kOSKextVersionStageInvalid
) {
334 num_digits_scanned
= 0;
336 /* Scan for stage level number. Legal characters are
339 while (num_digits_scanned
< VERS_STAGE_LEVEL_DIGITS
) {
340 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
341 if (num_digits_scanned
) {
346 } else if (__vers_isdigit(*current_char_p
)) {
347 vers_digit
= __vers_digit_for_char(*current_char_p
);
348 if (vers_digit
< 0) {
351 vers_stage_level
= (vers_stage_level
) * 10 + vers_digit
;
353 num_digits_scanned
++;
359 /* Check for too many digits.
361 if ((num_digits_scanned
== VERS_STAGE_LEVEL_DIGITS
) &&
362 ! (__vers_isspace(*current_char_p
) || (*current_char_p
== '\0'))) {
367 if (vers_stage_level
> VERS_STAGE_LEVEL_MAX
) {
373 if (vers_stage
== kOSKextVersionStageCandidate
&& vers_stage_level
== 0) {
377 result
= (vers_major
* VERS_MAJOR_MULT
) +
378 (vers_minor
* VERS_MINOR_MULT
) +
379 (vers_revision
* VERS_REVISION_MULT
) +
380 (vers_stage
* VERS_STAGE_MULT
) +
386 /*********************************************************************
387 * This function must be safe to call in panic context.
388 *********************************************************************/
389 Boolean
OSKextVersionGetString(
390 OSKextVersion aVersion
,
392 uint32_t bufferLength
)
395 OSKextVersion vers_major
= 0;
396 OSKextVersion vers_minor
= 0;
397 OSKextVersion vers_revision
= 0;
398 OSKextVersion vers_stage
= 0;
399 OSKextVersion vers_stage_level
= 0;
400 const char * stage_string
= NULL
; // don't free
402 /* No buffer or length less than longest possible vers string,
405 if (!buffer
|| bufferLength
< kOSKextVersionMaxLength
) {
409 bzero(buffer
, bufferLength
* sizeof(char));
412 strlcpy(buffer
, "(invalid)", bufferLength
);
416 strlcpy(buffer
, "(missing)", bufferLength
);
420 vers_major
= aVersion
/ VERS_MAJOR_MULT
;
421 if (vers_major
> VERS_MAJOR_MAX
) {
422 strlcpy(buffer
, "(invalid)", bufferLength
);
426 vers_minor
= aVersion
- (vers_major
* VERS_MAJOR_MULT
);
427 vers_minor
/= VERS_MINOR_MULT
;
429 vers_revision
= aVersion
-
430 ( (vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
) );
431 vers_revision
/= VERS_REVISION_MULT
;
433 vers_stage
= aVersion
-
434 ( (vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
) +
435 (vers_revision
* VERS_REVISION_MULT
));
436 vers_stage
/= VERS_STAGE_MULT
;
438 vers_stage_level
= aVersion
-
439 ( (vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
) +
440 (vers_revision
* VERS_REVISION_MULT
) + (vers_stage
* VERS_STAGE_MULT
));
441 if (vers_stage_level
> VERS_STAGE_LEVEL_MAX
) {
442 strlcpy(buffer
, "(invalid)", bufferLength
);
446 cpos
= snprintf(buffer
, bufferLength
, "%u", (uint32_t)vers_major
);
448 /* Always include the minor version; it just looks weird without.
452 cpos
+= snprintf(buffer
+cpos
, bufferLength
- cpos
, "%u", (uint32_t)vers_minor
);
454 /* The revision is displayed only if nonzero.
459 cpos
+= snprintf(buffer
+cpos
, bufferLength
- cpos
, "%u",
460 (uint32_t)vers_revision
);
463 stage_string
= __OSKextVersionStringForStage(vers_stage
);
465 strlcpy(buffer
, "(invalid)", bufferLength
);
468 if (stage_string
[0]) {
469 strlcat(buffer
, stage_string
, bufferLength
);
470 cpos
+= strlen(stage_string
);
473 if (vers_stage
< kOSKextVersionStageRelease
) {
474 snprintf(buffer
+cpos
, bufferLength
- cpos
, "%u", (uint32_t)vers_stage_level
);
480 /*********************************************************************
481 *********************************************************************/
483 OSKextVersion
OSKextParseVersionCFString(CFStringRef versionString
)
485 OSKextVersion result
= -1;
486 char versBuffer
[kOSKextVersionMaxLength
];
488 if (CFStringGetCString(versionString
, versBuffer
,
489 sizeof(versBuffer
), kCFStringEncodingASCII
)) {
491 result
= OSKextParseVersionString(versBuffer
);