X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/e2fac8b15b12a7979f72090454d850e612fc5b13..b0d623f7f2ae71ed96e60569f61f9a9a27016e80:/libkern/OSKextVersion.c diff --git a/libkern/OSKextVersion.c b/libkern/OSKextVersion.c new file mode 100644 index 000000000..bc1cc253c --- /dev/null +++ b/libkern/OSKextVersion.c @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2000-2008 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +#ifdef KERNEL +#include +#include +#include +#else +#include +#include +#include +#endif /* KERNEL */ + +#include + +#define VERS_MAJOR_DIGITS (4) +#define VERS_MINOR_DIGITS (2) +#define VERS_REVISION_DIGITS (2) +#define VERS_STAGE_DIGITS (1) +#define VERS_STAGE_LEVEL_DIGITS (3) + +#define VERS_MAJOR_MAX (9999) +#define VERS_STAGE_LEVEL_MAX (255) + +#define VERS_MAJOR_MULT (100000000) +#define VERS_MINOR_MULT (1000000) +#define VERS_REVISION_MULT (10000) +#define VERS_STAGE_MULT (1000) + + +typedef enum { + kOSKextVersionStageInvalid = 0, + kOSKextVersionStageDevelopment = 1, + kOSKextVersionStageAlpha = 3, + kOSKextVersionStageBeta = 5, + kOSKextVersionStageCandidate = 7, + kOSKextVersionStageRelease = 9, +} OSKextVersionStage; + + +/********************************************************************* +*********************************************************************/ +static int __vers_isdigit(char c) { + return (c == '0' || + c == '1' || c == '2' || c == '3' || + c == '4' || c == '5' || c == '6' || + c == '7' || c == '8' || c == '9'); +} + +/********************************************************************* +*********************************************************************/ +static int __vers_isspace(char c) { + return (c == ' ' || + c == '\t' || + c == '\r' || + c == '\n'); +} + +/********************************************************************* +*********************************************************************/ +static int __vers_digit_for_char(char c) { + switch (c) { + case '0': return 0; break; + case '1': return 1; break; + case '2': return 2; break; + case '3': return 3; break; + case '4': return 4; break; + case '5': return 5; break; + case '6': return 6; break; + case '7': return 7; break; + case '8': return 8; break; + case '9': return 9; break; + default: return -1; break; + } + + return -1; +} + +/********************************************************************* +*********************************************************************/ +static int __VERS_isreleasestate(char c) { + return (c == 'd' || c == 'a' || c == 'b' || c == 'f'); +} + + +/********************************************************************* +*********************************************************************/ +static OSKextVersionStage __OSKextVersionStageForString(const char ** string_p) { + const char * string; + + if (!string_p || !*string_p) { + return kOSKextVersionStageInvalid; + } + + string = *string_p; + + if (__vers_isspace(string[0]) || string[0] == '\0') { + return kOSKextVersionStageRelease; + } else { + switch (string[0]) { + case 'd': + if (__vers_isdigit(string[1])) { + *string_p = &string[1]; + return kOSKextVersionStageDevelopment; + } + break; + case 'a': + if (__vers_isdigit(string[1])) { + *string_p = &string[1]; + return kOSKextVersionStageAlpha; + } + break; + case 'b': + if (__vers_isdigit(string[1])) { + *string_p = &string[1]; + return kOSKextVersionStageBeta; + } + break; + case 'f': + if (__vers_isdigit(string[1])) { + *string_p = &string[1]; + return kOSKextVersionStageCandidate; + } else if (string[1] == 'c' && __vers_isdigit(string[2])) { + *string_p = &string[2]; + return kOSKextVersionStageCandidate; + } else { + return kOSKextVersionStageInvalid; + } + break; + default: + return kOSKextVersionStageInvalid; + break; + } + } + + return kOSKextVersionStageInvalid; +} + +/********************************************************************* +*********************************************************************/ +static const char * __OSKextVersionStringForStage(OSKextVersionStage stage) +{ + switch (stage) { + case kOSKextVersionStageInvalid: return NULL; break; + case kOSKextVersionStageDevelopment: return "d"; break; + case kOSKextVersionStageAlpha: return "a"; break; + case kOSKextVersionStageBeta: return "b"; break; + case kOSKextVersionStageCandidate: return "f"; break; + case kOSKextVersionStageRelease: return ""; break; + } + + return NULL; +} + +/********************************************************************* +*********************************************************************/ +OSKextVersion OSKextParseVersionString(const char * versionString) +{ + OSKextVersion result = -1; + int vers_digit = -1; + int num_digits_scanned = 0; + OSKextVersion vers_major = 0; + OSKextVersion vers_minor = 0; + OSKextVersion vers_revision = 0; + OSKextVersion vers_stage = 0; + OSKextVersion vers_stage_level = 0; + const char * current_char_p; + + if (!versionString || *versionString == '\0') { + return -1; + } + + current_char_p = (const char *)&versionString[0]; + + /***** + * Check for an initial digit of the major release number. + */ + vers_major = __vers_digit_for_char(*current_char_p); + if (vers_major < 0) { + return -1; + } + + current_char_p++; + num_digits_scanned = 1; + + /* Complete scan for major version number. Legal characters are + * any digit, period, any buildstage letter. + */ + while (num_digits_scanned < VERS_MAJOR_DIGITS) { + if (__vers_isspace(*current_char_p) || *current_char_p == '\0') { + vers_stage = kOSKextVersionStageRelease; + goto finish; + } else if (__vers_isdigit(*current_char_p)) { + vers_digit = __vers_digit_for_char(*current_char_p); + if (vers_digit < 0) { + return -1; + } + vers_major = (vers_major) * 10 + vers_digit; + current_char_p++; + num_digits_scanned++; + } else if (__VERS_isreleasestate(*current_char_p)) { + goto release_state; + } else if (*current_char_p == '.') { + current_char_p++; + goto minor_version; + } else { + return -1; + } + } + + /* Check for too many digits. + */ + if (num_digits_scanned == VERS_MAJOR_DIGITS) { + if (*current_char_p == '.') { + current_char_p++; + } else if (__vers_isdigit(*current_char_p)) { + return -1; + } + } + +minor_version: + + num_digits_scanned = 0; + + /* Scan for minor version number. Legal characters are + * any digit, period, any buildstage letter. + */ + while (num_digits_scanned < VERS_MINOR_DIGITS) { + if (__vers_isspace(*current_char_p) || *current_char_p == '\0') { + vers_stage = kOSKextVersionStageRelease; + goto finish; + } else if (__vers_isdigit(*current_char_p)) { + vers_digit = __vers_digit_for_char(*current_char_p); + if (vers_digit < 0) { + return -1; + } + vers_minor = (vers_minor) * 10 + vers_digit; + current_char_p++; + num_digits_scanned++; + } else if (__VERS_isreleasestate(*current_char_p)) { + goto release_state; + } else if (*current_char_p == '.') { + current_char_p++; + goto revision; + } else { + return -1; + } + } + + /* Check for too many digits. + */ + if (num_digits_scanned == VERS_MINOR_DIGITS) { + if (*current_char_p == '.') { + current_char_p++; + } else if (__vers_isdigit(*current_char_p)) { + return -1; + } + } + +revision: + + num_digits_scanned = 0; + + /* Scan for revision version number. Legal characters are + * any digit, any buildstage letter (NOT PERIOD). + */ + while (num_digits_scanned < VERS_REVISION_DIGITS) { + if (__vers_isspace(*current_char_p) || *current_char_p == '\0') { + vers_stage = kOSKextVersionStageRelease; + goto finish; + } else if (__vers_isdigit(*current_char_p)) { + vers_digit = __vers_digit_for_char(*current_char_p); + if (vers_digit < 0) { + return -1; + } + vers_revision = (vers_revision) * 10 + vers_digit; + current_char_p++; + num_digits_scanned++; + } else if (__VERS_isreleasestate(*current_char_p)) { + goto release_state; + } else { + return -1; + } + } + + /* Check for too many digits. + */ + if (num_digits_scanned == VERS_REVISION_DIGITS) { + if (*current_char_p == '.') { + current_char_p++; + } else if (__vers_isdigit(*current_char_p)) { + return -1; + } + } + +release_state: + + /***** + * Check for the release state. + */ + if (__vers_isspace(*current_char_p) || *current_char_p == '\0') { + vers_stage = kOSKextVersionStageRelease; + goto finish; + } else { + vers_stage = __OSKextVersionStageForString(¤t_char_p); + if (vers_stage == kOSKextVersionStageInvalid) { + return -1; + } + } + + +// stage level + + num_digits_scanned = 0; + + /* Scan for stage level number. Legal characters are + * any digit only. + */ + while (num_digits_scanned < VERS_STAGE_LEVEL_DIGITS) { + if (__vers_isspace(*current_char_p) || *current_char_p == '\0') { + if (num_digits_scanned) { + goto finish; + } else { + return -1; + } + } else if (__vers_isdigit(*current_char_p)) { + vers_digit = __vers_digit_for_char(*current_char_p); + if (vers_digit < 0) { + return -1; + } + vers_stage_level = (vers_stage_level) * 10 + vers_digit; + current_char_p++; + num_digits_scanned++; + } else { + return -1; + } + } + + /* Check for too many digits. + */ + if ((num_digits_scanned == VERS_STAGE_LEVEL_DIGITS) && + ! (__vers_isspace(*current_char_p) || (*current_char_p == '\0'))) { + + return -1; + } + + if (vers_stage_level > VERS_STAGE_LEVEL_MAX) { + return -1; + } + +finish: + + if (vers_stage == kOSKextVersionStageCandidate && vers_stage_level == 0) { + return -1; + } + + result = (vers_major * VERS_MAJOR_MULT) + + (vers_minor * VERS_MINOR_MULT) + + (vers_revision * VERS_REVISION_MULT) + + (vers_stage * VERS_STAGE_MULT) + + vers_stage_level; + + return result; +} + +/********************************************************************* +*********************************************************************/ +Boolean OSKextVersionGetString( + OSKextVersion aVersion, + char * buffer, + uint32_t bufferLength) +{ + int cpos = 0; + OSKextVersion vers_major = 0; + OSKextVersion vers_minor = 0; + OSKextVersion vers_revision = 0; + OSKextVersion vers_stage = 0; + OSKextVersion vers_stage_level = 0; + const char * stage_string = NULL; // don't free + + /* No buffer or length less than longest possible vers string, + * return 0. + */ + if (!buffer || bufferLength < kOSKextVersionMaxLength) { + return FALSE; + } + + bzero(buffer, bufferLength * sizeof(char)); + + if (aVersion < 0) { + strlcpy(buffer, "(invalid)", bufferLength); + return TRUE; + } + if (aVersion == 0) { + strlcpy(buffer, "(missing)", bufferLength); + return TRUE; + } + + vers_major = aVersion / VERS_MAJOR_MULT; + if (vers_major > VERS_MAJOR_MAX) { + strlcpy(buffer, "(invalid)", bufferLength); + return TRUE; + } + + vers_minor = aVersion - (vers_major * VERS_MAJOR_MULT); + vers_minor /= VERS_MINOR_MULT; + + vers_revision = aVersion - + ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) ); + vers_revision /= VERS_REVISION_MULT; + + vers_stage = aVersion - + ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) + + (vers_revision * VERS_REVISION_MULT)); + vers_stage /= VERS_STAGE_MULT; + + vers_stage_level = aVersion - + ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) + + (vers_revision * VERS_REVISION_MULT) + (vers_stage * VERS_STAGE_MULT)); + if (vers_stage_level > VERS_STAGE_LEVEL_MAX) { + strlcpy(buffer, "(invalid)", bufferLength); + return TRUE; + } + + cpos = snprintf(buffer, bufferLength, "%u", (uint32_t)vers_major); + + /* Always include the minor version; it just looks weird without. + */ + buffer[cpos] = '.'; + cpos++; + cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_minor); + + /* The revision is displayed only if nonzero. + */ + if (vers_revision) { + buffer[cpos] = '.'; + cpos++; + cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u", + (uint32_t)vers_revision); + } + + stage_string = __OSKextVersionStringForStage(vers_stage); + if (!stage_string) { + strlcpy(buffer, "(invalid)", bufferLength); + return TRUE; + } + if (stage_string[0]) { + strlcat(buffer, stage_string, bufferLength); + cpos += strlen(stage_string); + } + + if (vers_stage < kOSKextVersionStageRelease) { + snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_stage_level); + } + + return TRUE; +} + +/********************************************************************* +*********************************************************************/ +#ifndef KERNEL +OSKextVersion OSKextParseVersionCFString(CFStringRef versionString) +{ + OSKextVersion result = -1; + char versBuffer[kOSKextVersionMaxLength]; + + if (CFStringGetCString(versionString, versBuffer, + sizeof(versBuffer), kCFStringEncodingASCII)) { + + result = OSKextParseVersionString(versBuffer); + } + return result; +} +#endif