]> git.saurik.com Git - apple/xnu.git/blob - libsa/vers_rsrc.c
65dc005a1c164fde564fb4dc56417d6e792fd34d
[apple/xnu.git] / libsa / vers_rsrc.c
1 #include <libsa/vers_rsrc.h>
2 #include <sys/systm.h>
3
4
5 int isdigit(char c) {
6 return (c == '0' ||
7 c == '1' || c == '2' || c == '3' ||
8 c == '4' || c == '5' || c == '6' ||
9 c == '7' || c == '8' || c == '9');
10 }
11
12 int isspace(char c) {
13 return (c == ' ' ||
14 c == '\t' ||
15 c == '\r' ||
16 c == '\n');
17 }
18
19
20 int isreleasestate(char c) {
21 return (c == 'd' || c == 'a' || c == 'b' || c == 'f');
22 }
23
24
25 UInt8 BCD_digit_for_char(char c) {
26 switch (c) {
27 case '0': return 0; break;
28 case '1': return 1; break;
29 case '2': return 2; break;
30 case '3': return 3; break;
31 case '4': return 4; break;
32 case '5': return 5; break;
33 case '6': return 6; break;
34 case '7': return 7; break;
35 case '8': return 8; break;
36 case '9': return 9; break;
37 default: return BCD_illegal; break;
38 }
39 return BCD_illegal;
40 }
41
42
43 char BCD_char_for_digit(UInt8 digit) {
44 switch (digit) {
45 case 0: return '0'; break;
46 case 1: return '1'; break;
47 case 2: return '2'; break;
48 case 3: return '3'; break;
49 case 4: return '4'; break;
50 case 5: return '5'; break;
51 case 6: return '6'; break;
52 case 7: return '7'; break;
53 case 8: return '8'; break;
54 case 9: return '9'; break;
55 default: return '?'; break;
56 }
57 return '?';
58 }
59
60
61 VERS_revision VERS_revision_for_string(char ** string_p) {
62 char * string;
63
64 if (!string_p || !*string_p) {
65 return VERS_invalid;
66 }
67
68 string = *string_p;
69
70 if (isspace(string[0]) || string[0] == '\0') {
71 return VERS_release;
72 } else {
73 switch (string[0]) {
74 case 'd':
75 if (isdigit(string[1])) {
76 *string_p = &string[1];
77 return VERS_development;
78 }
79 break;
80 case 'a':
81 if (isdigit(string[1])) {
82 *string_p = &string[1];
83 return VERS_alpha;
84 }
85 break;
86 case 'b':
87 if (isdigit(string[1])) {
88 *string_p = &string[1];
89 return VERS_beta;
90 }
91 break;
92 case 'f':
93 if (isdigit(string[1])) {
94 *string_p = &string[1];
95 return VERS_candidate;
96 } else if (string[1] == 'c' && isdigit(string[2])) {
97 *string_p = &string[2];
98 return VERS_candidate;
99 } else {
100 return VERS_invalid;
101 }
102 break;
103 default:
104 return VERS_invalid;
105 break;
106 }
107 }
108
109 return VERS_invalid;
110 }
111
112
113 int VERS_parse_string(const char * vers_string, UInt32 * version_num) {
114 int result = 1;
115 VERS_version vers;
116 char * current_char_p;
117 UInt8 scratch;
118
119 if (!vers_string || *vers_string == '\0') {
120 return 0;
121 }
122
123 vers.vnum = 0;
124
125 current_char_p = &vers_string[0];
126
127
128 /*****
129 * Check for an initial digit of the major release number.
130 */
131 vers.bytes[0] = BCD_digit_for_char(*current_char_p);
132 if (vers.bytes[0] == BCD_illegal) {
133 return 0;
134 }
135
136 current_char_p++;
137
138
139 /*****
140 * Check for a second digit of the major release number.
141 */
142 if (*current_char_p == '\0') {
143 vers.bytes[2] = VERS_release;
144 vers.bytes[3] = 0xff;
145 goto finish;
146 } else if (isdigit(*current_char_p)) {
147 scratch = BCD_digit_for_char(*current_char_p);
148 if (scratch == BCD_illegal) {
149 return 0;
150 }
151 vers.bytes[0] = BCD_combine(vers.bytes[0], scratch);
152 current_char_p++;
153
154 if (*current_char_p == '\0') {
155 vers.bytes[2] = VERS_release;
156 vers.bytes[3] = 0xff;
157 goto finish;
158 } else if (isreleasestate(*current_char_p)) {
159 goto release_state;
160 } else if (*current_char_p == '.') {
161 current_char_p++;
162 } else {
163 return 0;
164 }
165 } else if (isreleasestate(*current_char_p)) {
166 goto release_state;
167 } else if (*current_char_p == '.') {
168 current_char_p++;
169 } else {
170 return 0;
171 }
172
173
174 /*****
175 * Check for the minor release number.
176 */
177 if (*current_char_p == '\0') {
178 vers.bytes[2] = VERS_release;
179 vers.bytes[3] = 0xff;
180 goto finish;
181 } else if (isdigit(*current_char_p)) {
182 vers.bytes[1] = BCD_digit_for_char(*current_char_p);
183 if (vers.bytes[1] == BCD_illegal) {
184 return 0;
185 }
186
187 // Make sure its the first nibble of byte 1!
188 vers.bytes[1] = BCD_combine(vers.bytes[1], 0);
189
190 current_char_p++;
191
192 if (*current_char_p == '\0') {
193 vers.bytes[2] = VERS_release;
194 vers.bytes[3] = 0xff;
195 goto finish;
196 } else if (isreleasestate(*current_char_p)) {
197 goto release_state;
198 } else if (*current_char_p == '.') {
199 current_char_p++;
200 } else {
201 return 0;
202 }
203 } else {
204 return 0;
205 }
206
207
208 /*****
209 * Check for the bugfix number.
210 */
211 if (*current_char_p == '\0') {
212 vers.bytes[2] = VERS_release;
213 vers.bytes[3] = 0xff;
214 goto finish;
215 } else if (isdigit(*current_char_p)) {
216 scratch = BCD_digit_for_char(*current_char_p);
217 if (scratch == BCD_illegal) {
218 return 0;
219 }
220
221 /* vers.bytes[1] has its left nibble set already */
222 vers.bytes[1] = vers.bytes[1] | scratch;
223
224 current_char_p++;
225
226 if (*current_char_p == '\0') {
227 vers.bytes[2] = VERS_release;
228 vers.bytes[3] = 0xff;
229 goto finish;
230 } else if (isreleasestate(*current_char_p)) {
231 goto release_state;
232 } else {
233 return 0;
234 }
235 } else {
236 return 0;
237 }
238
239
240 release_state:
241
242 /*****
243 * Check for the release state.
244 */
245 if (*current_char_p == '\0') {
246 vers.bytes[2] = VERS_release;
247 vers.bytes[3] = 0xff;
248 goto finish;
249 } else {
250 vers.bytes[2] = VERS_revision_for_string(&current_char_p);
251 if (vers.bytes[2] == VERS_invalid) {
252 return 0;
253 }
254 }
255
256
257 /*****
258 * Get the nonrelease revision number (0..255).
259 */
260 if (vers.bytes[2] != VERS_release) {
261 UInt32 revision_num = 0;
262 int i;
263
264 if (*current_char_p == '\0' || !isdigit(*current_char_p)) {
265 return 0;
266 }
267 for (i = 0; i < 3 && *current_char_p != '\0'; i++, current_char_p++) {
268 UInt8 scratch_digit;
269 scratch_digit = BCD_digit_for_char(*current_char_p);
270 if (scratch_digit == BCD_illegal) {
271 return 0;
272 }
273 revision_num *= 10;
274 revision_num += scratch_digit;
275 }
276 if (isdigit(*current_char_p) || revision_num > 255) {
277 return 0;
278 }
279 vers.bytes[3] = (UInt8)revision_num;
280 }
281
282 if (vers.bytes[2] == VERS_release) {
283 vers.bytes[3] = 0xff;
284 } else {
285 if (vers.bytes[2] == VERS_candidate) {
286 if (vers.bytes[3] == 0) {
287 return 0;
288 } else {
289 vers.bytes[2] = VERS_release;
290 vers.bytes[3]--;
291 }
292 }
293 }
294
295 finish:
296 *version_num = vers.vnum;
297 return result;
298 }
299
300
301 #define VERS_STRING_MAX_LEN (12)
302
303 int VERS_string(char * buffer, UInt32 length, UInt32 vers) {
304 VERS_version version;
305 int cpos = 0;
306 int result = 1;
307
308 char major1;
309 char major2;
310 char minor;
311 char bugfix;
312
313 version.vnum = vers;
314
315 /* No buffer, length less than longest possible vers string,
316 * return 0.
317 */
318 if (!buffer || length < VERS_STRING_MAX_LEN) {
319 result = -1;
320 goto finish;
321 }
322
323 bzero(buffer, length * sizeof(char));
324
325
326 /*****
327 * Major version number.
328 */
329 major1 = BCD_char_for_digit(BCD_get_left(version.bytes[0]));
330 if (major1 == '?') {
331 result = 0;
332 } /* this is not an 'else' situation */
333 if (major1 != '0') {
334 buffer[cpos] = major1;
335 cpos++;
336 }
337
338 major2 = BCD_char_for_digit(BCD_get_right(version.bytes[0]));
339 if (major2 == '?') {
340 result = 0;
341 }
342
343 buffer[cpos] = major2;
344 cpos++;
345
346
347 /*****
348 * Minor & bug-fix version numbers.
349 */
350 minor = BCD_char_for_digit(BCD_get_left(version.bytes[1]));
351 if (minor == '?') {
352 result = 0;
353 }
354 bugfix = BCD_char_for_digit(BCD_get_right(version.bytes[1]));
355 if (bugfix == '?') {
356 result = 0;
357 }
358
359
360 /* Always display the minor version number.
361 */
362 buffer[cpos] = '.';
363 cpos++;
364 buffer[cpos] = minor;
365 cpos++;
366
367
368 /* Only display the bugfix version number if it's nonzero.
369 */
370 if (bugfix != '0') {
371 buffer[cpos] = '.';
372 cpos++;
373 buffer[cpos] = bugfix;
374 cpos++;
375 }
376
377
378 /* If the release state is final, we're done!
379 */
380 if (version.bytes[2] == VERS_release && version.bytes[3] == 255) {
381 result = 0;
382 goto finish;
383 }
384
385
386 /*****
387 * Do the release state and update level.
388 */
389 switch (version.bytes[2]) {
390 case VERS_development:
391 buffer[cpos] = 'd';
392 cpos++;
393 break;
394 case VERS_alpha:
395 buffer[cpos] = 'a';
396 cpos++;
397 break;
398 case VERS_beta:
399 buffer[cpos] = 'b';
400 cpos++;
401 break;
402 case VERS_release:
403 if (version.bytes[3] < 255) {
404 buffer[cpos] = 'f';
405 buffer[cpos+1] = 'c';
406 cpos += 2;
407 } else {
408 result = 1;
409 goto finish;
410 }
411 break;
412 default:
413 result = 0;
414 buffer[cpos] = '?';
415 cpos++;
416 break;
417 }
418
419 if (version.bytes[2] != VERS_release) {
420 sprintf(&buffer[cpos], "%d", version.bytes[3]);
421 } else {
422 if (version.bytes[3] < 255) {
423 sprintf(&buffer[cpos], "%d", version.bytes[3] + 1);
424 }
425 }
426
427 finish:
428 return result;
429 }