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