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