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(&current_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