2 * Copyright (c) 2000-2008 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 <System/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 (2)
42 #define VERS_REVISION_DIGITS (2)
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 (100000000)
50 #define VERS_MINOR_MULT (1000000)
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 *********************************************************************/
85 static int __vers_digit_for_char(char 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;
103 /*********************************************************************
104 *********************************************************************/
105 static int __VERS_isreleasestate(char c
) {
106 return (c
== 'd' || c
== 'a' || c
== 'b' || c
== 'f');
110 /*********************************************************************
111 *********************************************************************/
112 static OSKextVersionStage
__OSKextVersionStageForString(const char ** string_p
) {
115 if (!string_p
|| !*string_p
) {
116 return kOSKextVersionStageInvalid
;
121 if (__vers_isspace(string
[0]) || string
[0] == '\0') {
122 return kOSKextVersionStageRelease
;
126 if (__vers_isdigit(string
[1])) {
127 *string_p
= &string
[1];
128 return kOSKextVersionStageDevelopment
;
132 if (__vers_isdigit(string
[1])) {
133 *string_p
= &string
[1];
134 return kOSKextVersionStageAlpha
;
138 if (__vers_isdigit(string
[1])) {
139 *string_p
= &string
[1];
140 return kOSKextVersionStageBeta
;
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
;
151 return kOSKextVersionStageInvalid
;
155 return kOSKextVersionStageInvalid
;
160 return kOSKextVersionStageInvalid
;
163 /*********************************************************************
164 *********************************************************************/
165 static const char * __OSKextVersionStringForStage(OSKextVersionStage 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;
179 /*********************************************************************
180 *********************************************************************/
181 OSKextVersion
OSKextParseVersionString(const char * versionString
)
183 OSKextVersion result
= -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
;
193 if (!versionString
|| *versionString
== '\0') {
197 current_char_p
= (const char *)&versionString
[0];
200 * Check for an initial digit of the major release number.
202 vers_major
= __vers_digit_for_char(*current_char_p
);
203 if (vers_major
< 0) {
208 num_digits_scanned
= 1;
210 /* Complete scan for major version number. Legal characters are
211 * any digit, period, any buildstage letter.
213 while (num_digits_scanned
< VERS_MAJOR_DIGITS
) {
214 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
215 vers_stage
= kOSKextVersionStageRelease
;
217 } else if (__vers_isdigit(*current_char_p
)) {
218 vers_digit
= __vers_digit_for_char(*current_char_p
);
219 if (vers_digit
< 0) {
222 vers_major
= (vers_major
) * 10 + vers_digit
;
224 num_digits_scanned
++;
225 } else if (__VERS_isreleasestate(*current_char_p
)) {
227 } else if (*current_char_p
== '.') {
235 /* Check for too many digits.
237 if (num_digits_scanned
== VERS_MAJOR_DIGITS
) {
238 if (*current_char_p
== '.') {
240 } else if (__vers_isdigit(*current_char_p
)) {
247 num_digits_scanned
= 0;
249 /* Scan for minor version number. Legal characters are
250 * any digit, period, any buildstage letter.
252 while (num_digits_scanned
< VERS_MINOR_DIGITS
) {
253 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
254 vers_stage
= kOSKextVersionStageRelease
;
256 } else if (__vers_isdigit(*current_char_p
)) {
257 vers_digit
= __vers_digit_for_char(*current_char_p
);
258 if (vers_digit
< 0) {
261 vers_minor
= (vers_minor
) * 10 + vers_digit
;
263 num_digits_scanned
++;
264 } else if (__VERS_isreleasestate(*current_char_p
)) {
266 } else if (*current_char_p
== '.') {
274 /* Check for too many digits.
276 if (num_digits_scanned
== VERS_MINOR_DIGITS
) {
277 if (*current_char_p
== '.') {
279 } else if (__vers_isdigit(*current_char_p
)) {
286 num_digits_scanned
= 0;
288 /* Scan for revision version number. Legal characters are
289 * any digit, any buildstage letter (NOT PERIOD).
291 while (num_digits_scanned
< VERS_REVISION_DIGITS
) {
292 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
293 vers_stage
= kOSKextVersionStageRelease
;
295 } else if (__vers_isdigit(*current_char_p
)) {
296 vers_digit
= __vers_digit_for_char(*current_char_p
);
297 if (vers_digit
< 0) {
300 vers_revision
= (vers_revision
) * 10 + vers_digit
;
302 num_digits_scanned
++;
303 } else if (__VERS_isreleasestate(*current_char_p
)) {
310 /* Check for too many digits.
312 if (num_digits_scanned
== VERS_REVISION_DIGITS
) {
313 if (*current_char_p
== '.') {
315 } else if (__vers_isdigit(*current_char_p
)) {
323 * Check for the release state.
325 if (__vers_isspace(*current_char_p
) || *current_char_p
== '\0') {
326 vers_stage
= kOSKextVersionStageRelease
;
329 vers_stage
= __OSKextVersionStageForString(¤t_char_p
);
330 if (vers_stage
== kOSKextVersionStageInvalid
) {
338 num_digits_scanned
= 0;
340 /* Scan for stage level number. Legal characters are
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
) {
350 } else if (__vers_isdigit(*current_char_p
)) {
351 vers_digit
= __vers_digit_for_char(*current_char_p
);
352 if (vers_digit
< 0) {
355 vers_stage_level
= (vers_stage_level
) * 10 + vers_digit
;
357 num_digits_scanned
++;
363 /* Check for too many digits.
365 if ((num_digits_scanned
== VERS_STAGE_LEVEL_DIGITS
) &&
366 ! (__vers_isspace(*current_char_p
) || (*current_char_p
== '\0'))) {
371 if (vers_stage_level
> VERS_STAGE_LEVEL_MAX
) {
377 if (vers_stage
== kOSKextVersionStageCandidate
&& vers_stage_level
== 0) {
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
) +
390 /*********************************************************************
391 * This function must be safe to call in panic context.
392 *********************************************************************/
393 Boolean
OSKextVersionGetString(
394 OSKextVersion aVersion
,
396 uint32_t bufferLength
)
399 OSKextVersion vers_major
= 0;
400 OSKextVersion vers_minor
= 0;
401 OSKextVersion vers_revision
= 0;
402 OSKextVersion vers_stage
= 0;
403 OSKextVersion vers_stage_level
= 0;
404 const char * stage_string
= NULL
; // don't free
406 /* No buffer or length less than longest possible vers string,
409 if (!buffer
|| bufferLength
< kOSKextVersionMaxLength
) {
413 bzero(buffer
, bufferLength
* sizeof(char));
416 strlcpy(buffer
, "(invalid)", bufferLength
);
420 strlcpy(buffer
, "(missing)", bufferLength
);
424 vers_major
= aVersion
/ VERS_MAJOR_MULT
;
425 if (vers_major
> VERS_MAJOR_MAX
) {
426 strlcpy(buffer
, "(invalid)", bufferLength
);
430 vers_minor
= aVersion
- (vers_major
* VERS_MAJOR_MULT
);
431 vers_minor
/= VERS_MINOR_MULT
;
433 vers_revision
= aVersion
-
434 ( (vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
) );
435 vers_revision
/= VERS_REVISION_MULT
;
437 vers_stage
= aVersion
-
438 ( (vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
) +
439 (vers_revision
* VERS_REVISION_MULT
));
440 vers_stage
/= VERS_STAGE_MULT
;
442 vers_stage_level
= aVersion
-
443 ( (vers_major
* VERS_MAJOR_MULT
) + (vers_minor
* VERS_MINOR_MULT
) +
444 (vers_revision
* VERS_REVISION_MULT
) + (vers_stage
* VERS_STAGE_MULT
));
445 if (vers_stage_level
> VERS_STAGE_LEVEL_MAX
) {
446 strlcpy(buffer
, "(invalid)", bufferLength
);
450 cpos
= snprintf(buffer
, bufferLength
, "%u", (uint32_t)vers_major
);
452 /* Always include the minor version; it just looks weird without.
456 cpos
+= snprintf(buffer
+cpos
, bufferLength
- cpos
, "%u", (uint32_t)vers_minor
);
458 /* The revision is displayed only if nonzero.
463 cpos
+= snprintf(buffer
+cpos
, bufferLength
- cpos
, "%u",
464 (uint32_t)vers_revision
);
467 stage_string
= __OSKextVersionStringForStage(vers_stage
);
469 strlcpy(buffer
, "(invalid)", bufferLength
);
472 if (stage_string
[0]) {
473 strlcat(buffer
, stage_string
, bufferLength
);
474 cpos
+= strlen(stage_string
);
477 if (vers_stage
< kOSKextVersionStageRelease
) {
478 snprintf(buffer
+cpos
, bufferLength
- cpos
, "%u", (uint32_t)vers_stage_level
);
484 /*********************************************************************
485 *********************************************************************/
487 OSKextVersion
OSKextParseVersionCFString(CFStringRef versionString
)
489 OSKextVersion result
= -1;
490 char versBuffer
[kOSKextVersionMaxLength
];
492 if (CFStringGetCString(versionString
, versBuffer
,
493 sizeof(versBuffer
), kCFStringEncodingASCII
)) {
495 result
= OSKextParseVersionString(versBuffer
);