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