]> git.saurik.com Git - apple/xnu.git/blame - libkern/OSKextVersion.c
xnu-3248.60.10.tar.gz
[apple/xnu.git] / libkern / OSKextVersion.c
CommitLineData
2d21ac55 1/*
b0d623f7 2 * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
2d21ac55
A
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 */
b0d623f7 28#ifdef KERNEL
1c79356b 29#include <sys/systm.h>
b0d623f7
A
30#include <libkern/OSKextLib.h>
31#include <libkern/OSKextLibPrivate.h>
55e303ae 32#else
b0d623f7 33#include <libc.h>
3e170ce0 34#include <libkern/OSKextLib.h>
b0d623f7
A
35#include <System/libkern/OSKextLibPrivate.h>
36#endif /* KERNEL */
37
38#include <libkern/OSKextLibPrivate.h>
55e303ae
A
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
b0d623f7
A
46#define VERS_MAJOR_MAX (9999)
47#define VERS_STAGE_LEVEL_MAX (255)
48
55e303ae
A
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
b0d623f7 54
55e303ae 55typedef enum {
b0d623f7
A
56 kOSKextVersionStageInvalid = 0,
57 kOSKextVersionStageDevelopment = 1,
58 kOSKextVersionStageAlpha = 3,
59 kOSKextVersionStageBeta = 5,
60 kOSKextVersionStageCandidate = 7,
61 kOSKextVersionStageRelease = 9,
62} OSKextVersionStage;
55e303ae
A
63
64
b0d623f7
A
65/*********************************************************************
66*********************************************************************/
55e303ae 67static int __vers_isdigit(char c) {
1c79356b
A
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
b0d623f7
A
74/*********************************************************************
75*********************************************************************/
55e303ae 76static int __vers_isspace(char c) {
1c79356b
A
77 return (c == ' ' ||
78 c == '\t' ||
79 c == '\r' ||
80 c == '\n');
81}
82
b0d623f7
A
83/*********************************************************************
84*********************************************************************/
55e303ae 85static int __vers_digit_for_char(char c) {
1c79356b
A
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;
55e303ae 97 default: return -1; break;
1c79356b 98 }
1c79356b 99
55e303ae
A
100 return -1;
101}
1c79356b 102
b0d623f7
A
103/*********************************************************************
104*********************************************************************/
55e303ae
A
105static int __VERS_isreleasestate(char c) {
106 return (c == 'd' || c == 'a' || c == 'b' || c == 'f');
1c79356b
A
107}
108
109
b0d623f7
A
110/*********************************************************************
111*********************************************************************/
112static OSKextVersionStage __OSKextVersionStageForString(const char ** string_p) {
2d21ac55 113 const char * string;
1c79356b
A
114
115 if (!string_p || !*string_p) {
b0d623f7 116 return kOSKextVersionStageInvalid;
1c79356b
A
117 }
118
119 string = *string_p;
120
55e303ae 121 if (__vers_isspace(string[0]) || string[0] == '\0') {
b0d623f7 122 return kOSKextVersionStageRelease;
1c79356b
A
123 } else {
124 switch (string[0]) {
125 case 'd':
55e303ae 126 if (__vers_isdigit(string[1])) {
1c79356b 127 *string_p = &string[1];
b0d623f7 128 return kOSKextVersionStageDevelopment;
1c79356b
A
129 }
130 break;
131 case 'a':
55e303ae 132 if (__vers_isdigit(string[1])) {
1c79356b 133 *string_p = &string[1];
b0d623f7 134 return kOSKextVersionStageAlpha;
1c79356b
A
135 }
136 break;
137 case 'b':
55e303ae 138 if (__vers_isdigit(string[1])) {
1c79356b 139 *string_p = &string[1];
b0d623f7 140 return kOSKextVersionStageBeta;
1c79356b
A
141 }
142 break;
143 case 'f':
55e303ae 144 if (__vers_isdigit(string[1])) {
1c79356b 145 *string_p = &string[1];
b0d623f7 146 return kOSKextVersionStageCandidate;
55e303ae 147 } else if (string[1] == 'c' && __vers_isdigit(string[2])) {
1c79356b 148 *string_p = &string[2];
b0d623f7 149 return kOSKextVersionStageCandidate;
1c79356b 150 } else {
b0d623f7 151 return kOSKextVersionStageInvalid;
1c79356b
A
152 }
153 break;
154 default:
b0d623f7 155 return kOSKextVersionStageInvalid;
1c79356b
A
156 break;
157 }
158 }
159
b0d623f7 160 return kOSKextVersionStageInvalid;
1c79356b
A
161}
162
b0d623f7
A
163/*********************************************************************
164*********************************************************************/
165static const char * __OSKextVersionStringForStage(OSKextVersionStage stage)
166{
55e303ae 167 switch (stage) {
b0d623f7
A
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;
55e303ae 174 }
1c79356b 175
b0d623f7 176 return NULL;
55e303ae
A
177}
178
b0d623f7
A
179/*********************************************************************
180*********************************************************************/
181OSKextVersion 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') {
55e303ae 194 return -1;
1c79356b
A
195 }
196
b0d623f7 197 current_char_p = (const char *)&versionString[0];
1c79356b
A
198
199 /*****
200 * Check for an initial digit of the major release number.
201 */
55e303ae
A
202 vers_major = __vers_digit_for_char(*current_char_p);
203 if (vers_major < 0) {
204 return -1;
1c79356b
A
205 }
206
207 current_char_p++;
55e303ae 208 num_digits_scanned = 1;
1c79356b 209
55e303ae
A
210 /* Complete scan for major version number. Legal characters are
211 * any digit, period, any buildstage letter.
1c79356b 212 */
55e303ae
A
213 while (num_digits_scanned < VERS_MAJOR_DIGITS) {
214 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
b0d623f7 215 vers_stage = kOSKextVersionStageRelease;
1c79356b 216 goto finish;
55e303ae
A
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)) {
1c79356b
A
226 goto release_state;
227 } else if (*current_char_p == '.') {
228 current_char_p++;
55e303ae 229 goto minor_version;
1c79356b 230 } else {
55e303ae 231 return -1;
1c79356b 232 }
1c79356b
A
233 }
234
55e303ae 235 /* Check for too many digits.
1c79356b 236 */
55e303ae
A
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;
1c79356b 242 }
55e303ae 243 }
1c79356b 244
55e303ae 245minor_version:
1c79356b 246
55e303ae 247 num_digits_scanned = 0;
1c79356b 248
55e303ae
A
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') {
b0d623f7 254 vers_stage = kOSKextVersionStageRelease;
1c79356b 255 goto finish;
55e303ae
A
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)) {
1c79356b
A
265 goto release_state;
266 } else if (*current_char_p == '.') {
267 current_char_p++;
55e303ae 268 goto revision;
1c79356b 269 } else {
55e303ae 270 return -1;
1c79356b 271 }
1c79356b
A
272 }
273
55e303ae 274 /* Check for too many digits.
1c79356b 275 */
55e303ae
A
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;
1c79356b 281 }
55e303ae 282 }
1c79356b 283
55e303ae 284revision:
1c79356b 285
55e303ae 286 num_digits_scanned = 0;
1c79356b 287
55e303ae
A
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') {
b0d623f7 293 vers_stage = kOSKextVersionStageRelease;
1c79356b 294 goto finish;
55e303ae
A
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)) {
1c79356b
A
304 goto release_state;
305 } else {
55e303ae 306 return -1;
1c79356b 307 }
1c79356b
A
308 }
309
55e303ae
A
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 }
1c79356b
A
319
320release_state:
321
322 /*****
323 * Check for the release state.
324 */
55e303ae 325 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
b0d623f7 326 vers_stage = kOSKextVersionStageRelease;
1c79356b
A
327 goto finish;
328 } else {
b0d623f7
A
329 vers_stage = __OSKextVersionStageForString(&current_char_p);
330 if (vers_stage == kOSKextVersionStageInvalid) {
55e303ae 331 return -1;
1c79356b
A
332 }
333 }
334
335
55e303ae 336// stage level
1c79356b 337
55e303ae 338 num_digits_scanned = 0;
1c79356b 339
55e303ae
A
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;
1c79356b 347 } else {
55e303ae
A
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;
1c79356b 354 }
55e303ae
A
355 vers_stage_level = (vers_stage_level) * 10 + vers_digit;
356 current_char_p++;
357 num_digits_scanned++;
358 } else {
359 return -1;
1c79356b
A
360 }
361 }
362
55e303ae
A
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'))) {
1c79356b 367
55e303ae
A
368 return -1;
369 }
1c79356b 370
b0d623f7 371 if (vers_stage_level > VERS_STAGE_LEVEL_MAX) {
55e303ae
A
372 return -1;
373 }
1c79356b 374
55e303ae 375finish:
1c79356b 376
b0d623f7 377 if (vers_stage == kOSKextVersionStageCandidate && vers_stage_level == 0) {
55e303ae
A
378 return -1;
379 }
1c79356b 380
55e303ae
A
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;
1c79356b 386
55e303ae
A
387 return result;
388}
389
b0d623f7 390/*********************************************************************
6d2010ae 391* This function must be safe to call in panic context.
b0d623f7
A
392*********************************************************************/
393Boolean OSKextVersionGetString(
394 OSKextVersion aVersion,
395 char * buffer,
396 uint32_t bufferLength)
397{
398 int cpos = 0;
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
55e303ae
A
405
406 /* No buffer or length less than longest possible vers string,
1c79356b
A
407 * return 0.
408 */
b0d623f7
A
409 if (!buffer || bufferLength < kOSKextVersionMaxLength) {
410 return FALSE;
1c79356b
A
411 }
412
b0d623f7 413 bzero(buffer, bufferLength * sizeof(char));
1c79356b 414
b0d623f7
A
415 if (aVersion < 0) {
416 strlcpy(buffer, "(invalid)", bufferLength);
417 return TRUE;
418 }
419 if (aVersion == 0) {
420 strlcpy(buffer, "(missing)", bufferLength);
421 return TRUE;
1c79356b
A
422 }
423
b0d623f7
A
424 vers_major = aVersion / VERS_MAJOR_MULT;
425 if (vers_major > VERS_MAJOR_MAX) {
426 strlcpy(buffer, "(invalid)", bufferLength);
427 return TRUE;
428 }
1c79356b 429
b0d623f7 430 vers_minor = aVersion - (vers_major * VERS_MAJOR_MULT);
55e303ae 431 vers_minor /= VERS_MINOR_MULT;
1c79356b 432
b0d623f7 433 vers_revision = aVersion -
55e303ae
A
434 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) );
435 vers_revision /= VERS_REVISION_MULT;
1c79356b 436
b0d623f7 437 vers_stage = aVersion -
55e303ae
A
438 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
439 (vers_revision * VERS_REVISION_MULT));
440 vers_stage /= VERS_STAGE_MULT;
441
b0d623f7 442 vers_stage_level = aVersion -
55e303ae
A
443 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
444 (vers_revision * VERS_REVISION_MULT) + (vers_stage * VERS_STAGE_MULT));
b0d623f7
A
445 if (vers_stage_level > VERS_STAGE_LEVEL_MAX) {
446 strlcpy(buffer, "(invalid)", bufferLength);
447 return TRUE;
448 }
1c79356b 449
b0d623f7 450 cpos = snprintf(buffer, bufferLength, "%u", (uint32_t)vers_major);
1c79356b 451
55e303ae 452 /* Always include the minor version; it just looks weird without.
1c79356b
A
453 */
454 buffer[cpos] = '.';
455 cpos++;
b0d623f7 456 cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_minor);
1c79356b 457
55e303ae 458 /* The revision is displayed only if nonzero.
1c79356b 459 */
55e303ae 460 if (vers_revision) {
1c79356b
A
461 buffer[cpos] = '.';
462 cpos++;
b0d623f7
A
463 cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u",
464 (uint32_t)vers_revision);
1c79356b
A
465 }
466
b0d623f7
A
467 stage_string = __OSKextVersionStringForStage(vers_stage);
468 if (!stage_string) {
469 strlcpy(buffer, "(invalid)", bufferLength);
470 return TRUE;
471 }
472 if (stage_string[0]) {
473 strlcat(buffer, stage_string, bufferLength);
55e303ae 474 cpos += strlen(stage_string);
1c79356b
A
475 }
476
b0d623f7
A
477 if (vers_stage < kOSKextVersionStageRelease) {
478 snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_stage_level);
1c79356b
A
479 }
480
b0d623f7
A
481 return TRUE;
482}
483
484/*********************************************************************
485*********************************************************************/
486#ifndef KERNEL
487OSKextVersion OSKextParseVersionCFString(CFStringRef versionString)
488{
489 OSKextVersion result = -1;
490 char versBuffer[kOSKextVersionMaxLength];
491
492 if (CFStringGetCString(versionString, versBuffer,
493 sizeof(versBuffer), kCFStringEncodingASCII)) {
494
495 result = OSKextParseVersionString(versBuffer);
496 }
497 return result;
1c79356b 498}
b0d623f7 499#endif