]> git.saurik.com Git - apple/xnu.git/blame - libkern/OSKextVersion.c
xnu-1504.15.3.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
A
33#include <libc.h>
34#include <System/libkern/OSKextLib.h>
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
A
390/*********************************************************************
391*********************************************************************/
392Boolean OSKextVersionGetString(
393 OSKextVersion aVersion,
394 char * buffer,
395 uint32_t bufferLength)
396{
397 int cpos = 0;
398 OSKextVersion vers_major = 0;
399 OSKextVersion vers_minor = 0;
400 OSKextVersion vers_revision = 0;
401 OSKextVersion vers_stage = 0;
402 OSKextVersion vers_stage_level = 0;
403 const char * stage_string = NULL; // don't free
55e303ae
A
404
405 /* No buffer or length less than longest possible vers string,
1c79356b
A
406 * return 0.
407 */
b0d623f7
A
408 if (!buffer || bufferLength < kOSKextVersionMaxLength) {
409 return FALSE;
1c79356b
A
410 }
411
b0d623f7 412 bzero(buffer, bufferLength * sizeof(char));
1c79356b 413
b0d623f7
A
414 if (aVersion < 0) {
415 strlcpy(buffer, "(invalid)", bufferLength);
416 return TRUE;
417 }
418 if (aVersion == 0) {
419 strlcpy(buffer, "(missing)", bufferLength);
420 return TRUE;
1c79356b
A
421 }
422
b0d623f7
A
423 vers_major = aVersion / VERS_MAJOR_MULT;
424 if (vers_major > VERS_MAJOR_MAX) {
425 strlcpy(buffer, "(invalid)", bufferLength);
426 return TRUE;
427 }
1c79356b 428
b0d623f7 429 vers_minor = aVersion - (vers_major * VERS_MAJOR_MULT);
55e303ae 430 vers_minor /= VERS_MINOR_MULT;
1c79356b 431
b0d623f7 432 vers_revision = aVersion -
55e303ae
A
433 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) );
434 vers_revision /= VERS_REVISION_MULT;
1c79356b 435
b0d623f7 436 vers_stage = aVersion -
55e303ae
A
437 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
438 (vers_revision * VERS_REVISION_MULT));
439 vers_stage /= VERS_STAGE_MULT;
440
b0d623f7 441 vers_stage_level = aVersion -
55e303ae
A
442 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
443 (vers_revision * VERS_REVISION_MULT) + (vers_stage * VERS_STAGE_MULT));
b0d623f7
A
444 if (vers_stage_level > VERS_STAGE_LEVEL_MAX) {
445 strlcpy(buffer, "(invalid)", bufferLength);
446 return TRUE;
447 }
1c79356b 448
b0d623f7 449 cpos = snprintf(buffer, bufferLength, "%u", (uint32_t)vers_major);
1c79356b 450
55e303ae 451 /* Always include the minor version; it just looks weird without.
1c79356b
A
452 */
453 buffer[cpos] = '.';
454 cpos++;
b0d623f7 455 cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_minor);
1c79356b 456
55e303ae 457 /* The revision is displayed only if nonzero.
1c79356b 458 */
55e303ae 459 if (vers_revision) {
1c79356b
A
460 buffer[cpos] = '.';
461 cpos++;
b0d623f7
A
462 cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u",
463 (uint32_t)vers_revision);
1c79356b
A
464 }
465
b0d623f7
A
466 stage_string = __OSKextVersionStringForStage(vers_stage);
467 if (!stage_string) {
468 strlcpy(buffer, "(invalid)", bufferLength);
469 return TRUE;
470 }
471 if (stage_string[0]) {
472 strlcat(buffer, stage_string, bufferLength);
55e303ae 473 cpos += strlen(stage_string);
1c79356b
A
474 }
475
b0d623f7
A
476 if (vers_stage < kOSKextVersionStageRelease) {
477 snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_stage_level);
1c79356b
A
478 }
479
b0d623f7
A
480 return TRUE;
481}
482
483/*********************************************************************
484*********************************************************************/
485#ifndef KERNEL
486OSKextVersion OSKextParseVersionCFString(CFStringRef versionString)
487{
488 OSKextVersion result = -1;
489 char versBuffer[kOSKextVersionMaxLength];
490
491 if (CFStringGetCString(versionString, versBuffer,
492 sizeof(versBuffer), kCFStringEncodingASCII)) {
493
494 result = OSKextParseVersionString(versBuffer);
495 }
496 return result;
1c79356b 497}
b0d623f7 498#endif