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/OSKextLibPrivate.h> 31#else 32#include <libc.h> 33#include <System/libkern/OSKextLibPrivate.h> 34#endif /* KERNEL */ 35#include <System/libkern/OSKextLibPrivate.h> 36 37// xxx - This file is duplicated in IOKitUser; would like to have it shared. 38 39#define VERS_MAJOR_DIGITS (4) 40#define VERS_MINOR_DIGITS (2) 41#define VERS_REVISION_DIGITS (2) 42#define VERS_STAGE_DIGITS (1) 43#define VERS_STAGE_LEVEL_DIGITS (3) 44 45#define VERS_MAJOR_MAX (9999) 46#define VERS_STAGE_LEVEL_MAX (255) 47 48#define VERS_MAJOR_MULT (100000000) 49#define VERS_MINOR_MULT (1000000) 50#define VERS_REVISION_MULT (10000) 51#define VERS_STAGE_MULT (1000) 52 53 54typedef enum { 55 kOSKextVersionStageInvalid = 0, 56 kOSKextVersionStageDevelopment = 1, 57 kOSKextVersionStageAlpha = 3, 58 kOSKextVersionStageBeta = 5, 59 kOSKextVersionStageCandidate = 7, 60 kOSKextVersionStageRelease = 9, 61} OSKextVersionStage; 62 63 64/********************************************************************* 65*********************************************************************/ 66static int __vers_isdigit(char c) { 67 return (c == '0' || 68 c == '1' || c == '2' || c == '3' || 69 c == '4' || c == '5' || c == '6' || 70 c == '7' || c == '8' || c == '9'); 71} 72 73/********************************************************************* 74*********************************************************************/ 75static int __vers_isspace(char c) { 76 return (c == ' ' || 77 c == '\t' || 78 c == '\r' || 79 c == '\n'); 80} 81 82/********************************************************************* 83*********************************************************************/ 84static int __vers_digit_for_char(char c) { 85 switch (c) { 86 case '0': return 0; break; 87 case '1': return 1; break; 88 case '2': return 2; break; 89 case '3': return 3; break; 90 case '4': return 4; break; 91 case '5': return 5; break; 92 case '6': return 6; break; 93 case '7': return 7; break; 94 case '8': return 8; break; 95 case '9': return 9; break; 96 default: return -1; break; 97 } 98 99 return -1; 100} 101 102/********************************************************************* 103*********************************************************************/ 104static int __VERS_isreleasestate(char c) { 105 return (c == 'd' || c == 'a' || c == 'b' || c == 'f'); 106} 107 108 109/********************************************************************* 110*********************************************************************/ 111static OSKextVersionStage __OSKextVersionStageForString(const char ** string_p) { 112 const char * string; 113 114 if (!string_p || !*string_p) { 115 return kOSKextVersionStageInvalid; 116 } 117 118 string = *string_p; 119 120 if (__vers_isspace(string[0]) || string[0] == '\0') { 121 return kOSKextVersionStageRelease; 122 } else { 123 switch (string[0]) { 124 case 'd': 125 if (__vers_isdigit(string[1])) { 126 *string_p = &string[1]; 127 return kOSKextVersionStageDevelopment; 128 } 129 break; 130 case 'a': 131 if (__vers_isdigit(string[1])) { 132 *string_p = &string[1]; 133 return kOSKextVersionStageAlpha; 134 } 135 break; 136 case 'b': 137 if (__vers_isdigit(string[1])) { 138 *string_p = &string[1]; 139 return kOSKextVersionStageBeta; 140 } 141 break; 142 case 'f': 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; 149 } else { 150 return kOSKextVersionStageInvalid; 151 } 152 break; 153 default: 154 return kOSKextVersionStageInvalid; 155 break; 156 } 157 } 158 159 return kOSKextVersionStageInvalid; 160} 161 162/********************************************************************* 163*********************************************************************/ 164static const char * __OSKextVersionStringForStage(OSKextVersionStage stage) 165{ 166 switch (stage) { 167 case kOSKextVersionStageInvalid: return NULL; break; 168 case kOSKextVersionStageDevelopment: return "d"; break; 169 case kOSKextVersionStageAlpha: return "a"; break; 170 case kOSKextVersionStageBeta: return "b"; break; 171 case kOSKextVersionStageCandidate: return "f"; break; 172 case kOSKextVersionStageRelease: return ""; break; 173 } 174 175 return NULL; 176} 177 178/********************************************************************* 179*********************************************************************/ 180OSKextVersion OSKextParseVersionString(const char * versionString) 181{ 182 OSKextVersion result = -1; 183 int vers_digit = -1; 184 int num_digits_scanned = 0; 185 OSKextVersion vers_major = 0; 186 OSKextVersion vers_minor = 0; 187 OSKextVersion vers_revision = 0; 188 OSKextVersion vers_stage = 0; 189 OSKextVersion vers_stage_level = 0; 190 const char * current_char_p; 191 192 if (!versionString || *versionString == '\0') { 193 return -1; 194 } 195 196 current_char_p = (const char *)&versionString[0]; 197 198 /***** 199 * Check for an initial digit of the major release number. 200 */ 201 vers_major = __vers_digit_for_char(*current_char_p); 202 if (vers_major < 0) { 203 return -1; 204 } 205 206 current_char_p++; 207 num_digits_scanned = 1; 208 209 /* Complete scan for major version number. Legal characters are 210 * any digit, period, any buildstage letter. 211 */ 212 while (num_digits_scanned < VERS_MAJOR_DIGITS) { 213 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') { 214 vers_stage = kOSKextVersionStageRelease; 215 goto finish; 216 } else if (__vers_isdigit(*current_char_p)) { 217 vers_digit = __vers_digit_for_char(*current_char_p); 218 if (vers_digit < 0) { 219 return -1; 220 } 221 vers_major = (vers_major) * 10 + vers_digit; 222 current_char_p++; 223 num_digits_scanned++; 224 } else if (__VERS_isreleasestate(*current_char_p)) { 225 goto release_state; 226 } else if (*current_char_p == '.') { 227 current_char_p++; 228 goto minor_version; 229 } else { 230 return -1; 231 } 232 } 233 234 /* Check for too many digits. 235 */ 236 if (num_digits_scanned == VERS_MAJOR_DIGITS) { 237 if (*current_char_p == '.') { 238 current_char_p++; 239 } else if (__vers_isdigit(*current_char_p)) { 240 return -1; 241 } 242 } 243 244minor_version: 245 246 num_digits_scanned = 0; 247 248 /* Scan for minor version number. Legal characters are 249 * any digit, period, any buildstage letter. 250 */ 251 while (num_digits_scanned < VERS_MINOR_DIGITS) { 252 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') { 253 vers_stage = kOSKextVersionStageRelease; 254 goto finish; 255 } else if (__vers_isdigit(*current_char_p)) { 256 vers_digit = __vers_digit_for_char(*current_char_p); 257 if (vers_digit < 0) { 258 return -1; 259 } 260 vers_minor = (vers_minor) * 10 + vers_digit; 261 current_char_p++; 262 num_digits_scanned++; 263 } else if (__VERS_isreleasestate(*current_char_p)) { 264 goto release_state; 265 } else if (*current_char_p == '.') { 266 current_char_p++; 267 goto revision; 268 } else { 269 return -1; 270 } 271 } 272 273 /* Check for too many digits. 274 */ 275 if (num_digits_scanned == VERS_MINOR_DIGITS) { 276 if (*current_char_p == '.') { 277 current_char_p++; 278 } else if (__vers_isdigit(*current_char_p)) { 279 return -1; 280 } 281 } 282 283revision: 284 285 num_digits_scanned = 0; 286 287 /* Scan for revision version number. Legal characters are 288 * any digit, any buildstage letter (NOT PERIOD). 289 */ 290 while (num_digits_scanned < VERS_REVISION_DIGITS) { 291 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') { 292 vers_stage = kOSKextVersionStageRelease; 293 goto finish; 294 } else if (__vers_isdigit(*current_char_p)) { 295 vers_digit = __vers_digit_for_char(*current_char_p); 296 if (vers_digit < 0) { 297 return -1; 298 } 299 vers_revision = (vers_revision) * 10 + vers_digit; 300 current_char_p++; 301 num_digits_scanned++; 302 } else if (__VERS_isreleasestate(*current_char_p)) { 303 goto release_state; 304 } else { 305 return -1; 306 } 307 } 308 309 /* Check for too many digits. 310 */ 311 if (num_digits_scanned == VERS_REVISION_DIGITS) { 312 if (*current_char_p == '.') { 313 current_char_p++; 314 } else if (__vers_isdigit(*current_char_p)) { 315 return -1; 316 } 317 } 318 319release_state: 320 321 /***** 322 * Check for the release state. 323 */ 324 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') { 325 vers_stage = kOSKextVersionStageRelease; 326 goto finish; 327 } else { 328 vers_stage = __OSKextVersionStageForString(¤t_char_p); 329 if (vers_stage == kOSKextVersionStageInvalid) { 330 return -1; 331 } 332 } 333 334 335// stage level 336 337 num_digits_scanned = 0; 338 339 /* Scan for stage level number. Legal characters are 340 * any digit only. 341 */ 342 while (num_digits_scanned < VERS_STAGE_LEVEL_DIGITS) { 343 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') { 344 if (num_digits_scanned) { 345 goto finish; 346 } else { 347 return -1; 348 } 349 } else if (__vers_isdigit(*current_char_p)) { 350 vers_digit = __vers_digit_for_char(*current_char_p); 351 if (vers_digit < 0) { 352 return -1; 353 } 354 vers_stage_level = (vers_stage_level) * 10 + vers_digit; 355 current_char_p++; 356 num_digits_scanned++; 357 } else { 358 return -1; 359 } 360 } 361 362 /* Check for too many digits. 363 */ 364 if ((num_digits_scanned == VERS_STAGE_LEVEL_DIGITS) && 365 ! (__vers_isspace(*current_char_p) || (*current_char_p == '\0'))) { 366 367 return -1; 368 } 369 370 if (vers_stage_level > VERS_STAGE_LEVEL_MAX) { 371 return -1; 372 } 373 374finish: 375 376 if (vers_stage == kOSKextVersionStageCandidate && vers_stage_level == 0) { 377 return -1; 378 } 379 380 result = (vers_major * VERS_MAJOR_MULT) + 381 (vers_minor * VERS_MINOR_MULT) + 382 (vers_revision * VERS_REVISION_MULT) + 383 (vers_stage * VERS_STAGE_MULT) + 384 vers_stage_level; 385 386 return result; 387} 388 389/********************************************************************* 390*********************************************************************/ 391Boolean OSKextVersionGetString( 392 OSKextVersion aVersion, 393 char * buffer, 394 uint32_t bufferLength) 395{ 396 int cpos = 0; 397 OSKextVersion vers_major = 0; 398 OSKextVersion vers_minor = 0; 399 OSKextVersion vers_revision = 0; 400 OSKextVersion vers_stage = 0; 401 OSKextVersion vers_stage_level = 0; 402 const char * stage_string = NULL; // don't free 403 404 /* No buffer or length less than longest possible vers string, 405 * return 0. 406 */ 407 if (!buffer || bufferLength < kOSKextVersionMaxLength) { 408 return FALSE; 409 } 410 411 bzero(buffer, bufferLength * sizeof(char)); 412 413 if (aVersion < 0) { 414 strlcpy(buffer, "(invalid)", bufferLength); 415 return TRUE; 416 } 417 if (aVersion == 0) { 418 strlcpy(buffer, "(missing)", bufferLength); 419 return TRUE; 420 } 421 422 vers_major = aVersion / VERS_MAJOR_MULT; 423 if (vers_major > VERS_MAJOR_MAX) { 424 strlcpy(buffer, "(invalid)", bufferLength); 425 return TRUE; 426 } 427 428 vers_minor = aVersion - (vers_major * VERS_MAJOR_MULT); 429 vers_minor /= VERS_MINOR_MULT; 430 431 vers_revision = aVersion - 432 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) ); 433 vers_revision /= VERS_REVISION_MULT; 434 435 vers_stage = aVersion - 436 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) + 437 (vers_revision * VERS_REVISION_MULT)); 438 vers_stage /= VERS_STAGE_MULT; 439 440 vers_stage_level = aVersion - 441 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) + 442 (vers_revision * VERS_REVISION_MULT) + (vers_stage * VERS_STAGE_MULT)); 443 if (vers_stage_level > VERS_STAGE_LEVEL_MAX) { 444 strlcpy(buffer, "(invalid)", bufferLength); 445 return TRUE; 446 } 447 448 cpos = snprintf(buffer, bufferLength, "%u", (uint32_t)vers_major); 449 450 /* Always include the minor version; it just looks weird without. 451 */ 452 buffer[cpos] = '.'; 453 cpos++; 454 cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_minor); 455 456 /* The revision is displayed only if nonzero. 457 */ 458 if (vers_revision) { 459 buffer[cpos] = '.'; 460 cpos++; 461 cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u", 462 (uint32_t)vers_revision); 463 } 464 465 stage_string = __OSKextVersionStringForStage(vers_stage); 466 if (!stage_string) { 467 strlcpy(buffer, "(invalid)", bufferLength); 468 return TRUE; 469 } 470 if (stage_string[0]) { 471 strlcat(buffer, stage_string, bufferLength); 472 cpos += strlen(stage_string); 473 } 474 475 if (vers_stage < kOSKextVersionStageRelease) { 476 snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_stage_level); 477 } 478 479 return TRUE; 480} 481 482/********************************************************************* 483*********************************************************************/ 484#ifndef KERNEL 485OSKextVersion OSKextParseVersionCFString(CFStringRef versionString) 486{ 487 OSKextVersion result = -1; 488 char versBuffer[kOSKextVersionMaxLength]; 489 490 if (CFStringGetCString(versionString, versBuffer, 491 sizeof(versBuffer), kCFStringEncodingASCII)) { 492 493 result = OSKextParseVersionString(versBuffer); 494 } 495 return result; 496} 497#endif 498