]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
55e303ae | 2 | * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. |
1c79356b | 3 | * |
8ad349bb | 4 | * @APPLE_LICENSE_OSREFERENCE_HEADER_START@ |
1c79356b | 5 | * |
8ad349bb A |
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 | |
10 | * License may not be used to create, or enable the creation or | |
11 | * redistribution of, unlawful or unlicensed copies of an Apple operating | |
12 | * system, or to circumvent, violate, or enable the circumvention or | |
13 | * violation of, any terms of an Apple operating system software license | |
14 | * agreement. | |
15 | * | |
16 | * Please obtain a copy of the License at | |
17 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
18 | * file. | |
19 | * | |
20 | * The Original Code and all software distributed under the License are | |
21 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
22 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
23 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
24 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
25 | * Please see the License for the specific language governing rights and | |
26 | * limitations under the License. | |
27 | * | |
28 | * @APPLE_LICENSE_OSREFERENCE_HEADER_END@ | |
1c79356b A |
29 | */ |
30 | ||
55e303ae A |
31 | /* |
32 | * HISTORY | |
33 | * | |
34 | * OSUnserializeXML.y created by rsulack on Tue Oct 12 1999 | |
35 | */ | |
1c79356b | 36 | |
55e303ae | 37 | // parser for unserializing OSContainer objects serialized to XML |
1c79356b A |
38 | // |
39 | // to build : | |
40 | // bison -p OSUnserializeXML OSUnserializeXML.y | |
41 | // head -50 OSUnserializeXML.y > OSUnserializeXML.cpp | |
55e303ae | 42 | // sed -e "s/#include <stdio.h>//" < OSUnserializeXML.tab.c >> OSUnserializeXML.cpp |
1c79356b A |
43 | // |
44 | // when changing code check in both OSUnserializeXML.y and OSUnserializeXML.cpp | |
45 | // | |
46 | // | |
47 | // | |
48 | // | |
49 | // | |
1c79356b A |
50 | // DO NOT EDIT OSUnserializeXML.cpp! |
51 | // | |
52 | // this means you! | |
53 | // | |
54 | // | |
55 | // | |
56 | // | |
57 | // | |
58 | // | |
1c79356b A |
59 | |
60 | ||
55e303ae A |
61 | %pure_parser |
62 | ||
1c79356b A |
63 | %{ |
64 | #include <string.h> | |
65 | #include <libkern/c++/OSMetaClass.h> | |
66 | #include <libkern/c++/OSContainers.h> | |
67 | #include <libkern/c++/OSLib.h> | |
68 | ||
55e303ae A |
69 | #define YYSTYPE object_t * |
70 | #define YYPARSE_PARAM state | |
5d5c5d0d | 71 | #define YYLEX_PARAM (parser_state_t *)state |
55e303ae A |
72 | |
73 | // this is the internal struct used to hold objects on parser stack | |
74 | // it represents objects both before and after they have been created | |
1c79356b A |
75 | typedef struct object { |
76 | struct object *next; | |
77 | struct object *free; | |
78 | struct object *elements; | |
79 | OSObject *object; | |
55e303ae | 80 | OSString *key; // for dictionary |
1c79356b | 81 | int size; |
55e303ae A |
82 | void *data; // for data |
83 | char *string; // for string & symbol | |
84 | long long number; // for number | |
1c79356b A |
85 | int idref; |
86 | } object_t; | |
87 | ||
55e303ae A |
88 | // this code is reentrant, this structure contains all |
89 | // state information for the parsing of a single buffer | |
90 | typedef struct parser_state { | |
91 | const char *parseBuffer; // start of text to be parsed | |
92 | int parseBufferIndex; // current index into text | |
93 | int lineNumber; // current line number | |
94 | object_t *objects; // internal objects in use | |
95 | object_t *freeObjects; // internal objects that are free | |
96 | OSDictionary *tags; // used to remember "ID" tags | |
97 | OSString **errorString; // parse error with line | |
98 | OSObject *parsedObject; // resultant object of parsed text | |
99 | } parser_state_t; | |
100 | ||
101 | #define STATE ((parser_state_t *)state) | |
102 | ||
103 | #undef yyerror | |
104 | #define yyerror(s) OSUnserializeerror(STATE, (s)) | |
105 | static int OSUnserializeerror(parser_state_t *state, char *s); | |
106 | ||
107 | static int yylex(YYSTYPE *lvalp, parser_state_t *state); | |
108 | static int yyparse(void * state); | |
109 | ||
110 | static object_t *newObject(parser_state_t *state); | |
111 | static void freeObject(parser_state_t *state, object_t *o); | |
112 | static void rememberObject(parser_state_t *state, int tag, OSObject *o); | |
113 | static object_t *retrieveObject(parser_state_t *state, int tag); | |
114 | static void cleanupObjects(parser_state_t *state); | |
115 | ||
116 | static object_t *buildDictionary(parser_state_t *state, object_t *o); | |
117 | static object_t *buildArray(parser_state_t *state, object_t *o); | |
118 | static object_t *buildSet(parser_state_t *state, object_t *o); | |
119 | static object_t *buildString(parser_state_t *state, object_t *o); | |
120 | static object_t *buildData(parser_state_t *state, object_t *o); | |
121 | static object_t *buildNumber(parser_state_t *state, object_t *o); | |
122 | static object_t *buildBoolean(parser_state_t *state, object_t *o); | |
1c79356b A |
123 | |
124 | extern "C" { | |
55e303ae A |
125 | extern void *kern_os_malloc(size_t size); |
126 | extern void *kern_os_realloc(void * addr, size_t size); | |
127 | extern void kern_os_free(void * addr); | |
1c79356b A |
128 | |
129 | //XXX shouldn't have to define these | |
55e303ae A |
130 | extern long strtol(const char *, char **, int); |
131 | extern unsigned long strtoul(const char *, char **, int); | |
1c79356b A |
132 | |
133 | } /* extern "C" */ | |
134 | ||
135 | #define malloc(s) kern_os_malloc(s) | |
136 | #define realloc(a, s) kern_os_realloc(a, s) | |
5d5c5d0d | 137 | #define free(a) kern_os_free((void *)a) |
1c79356b A |
138 | |
139 | %} | |
140 | %token ARRAY | |
141 | %token BOOLEAN | |
142 | %token DATA | |
143 | %token DICTIONARY | |
144 | %token IDREF | |
145 | %token KEY | |
146 | %token NUMBER | |
147 | %token SET | |
148 | %token STRING | |
149 | %token SYNTAX_ERROR | |
150 | %% /* Grammar rules and actions follow */ | |
151 | ||
55e303ae A |
152 | input: /* empty */ { yyerror("unexpected end of buffer"); |
153 | YYERROR; | |
154 | } | |
155 | | object { STATE->parsedObject = $1->object; | |
1c79356b | 156 | $1->object = 0; |
55e303ae | 157 | freeObject(STATE, $1); |
1c79356b A |
158 | YYACCEPT; |
159 | } | |
55e303ae | 160 | | SYNTAX_ERROR { yyerror("syntax error"); |
1c79356b A |
161 | YYERROR; |
162 | } | |
163 | ; | |
164 | ||
55e303ae A |
165 | object: dict { $$ = buildDictionary(STATE, $1); } |
166 | | array { $$ = buildArray(STATE, $1); } | |
167 | | set { $$ = buildSet(STATE, $1); } | |
168 | | string { $$ = buildString(STATE, $1); } | |
169 | | data { $$ = buildData(STATE, $1); } | |
170 | | number { $$ = buildNumber(STATE, $1); } | |
171 | | boolean { $$ = buildBoolean(STATE, $1); } | |
172 | | idref { $$ = retrieveObject(STATE, $1->idref); | |
1c79356b A |
173 | if ($$) { |
174 | $$->object->retain(); | |
175 | } else { | |
176 | yyerror("forward reference detected"); | |
177 | YYERROR; | |
178 | } | |
55e303ae | 179 | freeObject(STATE, $1); |
1c79356b A |
180 | } |
181 | ; | |
182 | ||
183 | //------------------------------------------------------------------------------ | |
184 | ||
185 | dict: '{' '}' { $$ = $1; | |
186 | $$->elements = NULL; | |
187 | } | |
188 | | '{' pairs '}' { $$ = $1; | |
189 | $$->elements = $2; | |
190 | } | |
191 | | DICTIONARY | |
192 | ; | |
193 | ||
194 | pairs: pair | |
195 | | pairs pair { $$ = $2; | |
196 | $$->next = $1; | |
197 | } | |
198 | ; | |
199 | ||
200 | pair: key object { $$ = $1; | |
5d5c5d0d | 201 | $$->key = (OSString *)$$->object; |
1c79356b | 202 | $$->object = $2->object; |
55e303ae | 203 | $$->next = NULL; |
1c79356b | 204 | $2->object = 0; |
55e303ae | 205 | freeObject(STATE, $2); |
1c79356b A |
206 | } |
207 | ; | |
208 | ||
55e303ae | 209 | key: KEY { $$ = buildString(STATE, $1); } |
1c79356b A |
210 | ; |
211 | ||
212 | //------------------------------------------------------------------------------ | |
213 | ||
214 | array: '(' ')' { $$ = $1; | |
215 | $$->elements = NULL; | |
216 | } | |
217 | | '(' elements ')' { $$ = $1; | |
218 | $$->elements = $2; | |
219 | } | |
220 | | ARRAY | |
221 | ; | |
222 | ||
223 | set: '[' ']' { $$ = $1; | |
224 | $$->elements = NULL; | |
225 | } | |
226 | | '[' elements ']' { $$ = $1; | |
227 | $$->elements = $2; | |
228 | } | |
229 | | SET | |
230 | ; | |
231 | ||
232 | elements: object { $$ = $1; | |
233 | $$->next = NULL; | |
234 | } | |
235 | | elements object { $$ = $2; | |
236 | $$->next = $1; | |
237 | } | |
238 | ; | |
239 | ||
240 | //------------------------------------------------------------------------------ | |
241 | ||
242 | boolean: BOOLEAN | |
243 | ; | |
244 | ||
245 | data: DATA | |
246 | ; | |
247 | ||
248 | idref: IDREF | |
249 | ; | |
250 | ||
251 | number: NUMBER | |
252 | ; | |
253 | ||
254 | string: STRING | |
255 | ; | |
256 | ||
257 | %% | |
1c79356b A |
258 | |
259 | int | |
55e303ae | 260 | OSUnserializeerror(parser_state_t * state, char *s) /* Called by yyparse on errors */ |
1c79356b | 261 | { |
55e303ae A |
262 | char tempString[128]; |
263 | ||
264 | if (state->errorString) { | |
265 | snprintf(tempString, 128, "OSUnserializeXML: %s near line %d\n", s, state->lineNumber); | |
266 | *(state->errorString) = OSString::withCString(tempString); | |
267 | } | |
268 | ||
269 | return 0; | |
1c79356b A |
270 | } |
271 | ||
272 | #define TAG_MAX_LENGTH 32 | |
273 | #define TAG_MAX_ATTRIBUTES 32 | |
274 | #define TAG_BAD 0 | |
275 | #define TAG_START 1 | |
276 | #define TAG_END 2 | |
277 | #define TAG_EMPTY 3 | |
278 | #define TAG_COMMENT 4 | |
279 | ||
55e303ae A |
280 | #define currentChar() (state->parseBuffer[state->parseBufferIndex]) |
281 | #define nextChar() (state->parseBuffer[++state->parseBufferIndex]) | |
282 | #define prevChar() (state->parseBuffer[state->parseBufferIndex - 1]) | |
283 | ||
284 | #define isSpace(c) ((c) == ' ' || (c) == '\t') | |
285 | #define isAlpha(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z')) | |
286 | #define isDigit(c) ((c) >= '0' && (c) <= '9') | |
287 | #define isAlphaDigit(c) ((c) >= 'a' && (c) <= 'f') | |
288 | #define isHexDigit(c) (isDigit(c) || isAlphaDigit(c)) | |
289 | #define isAlphaNumeric(c) (isAlpha(c) || isDigit(c) || ((c) == '-')) | |
290 | ||
1c79356b | 291 | static int |
55e303ae A |
292 | getTag(parser_state_t *state, |
293 | char tag[TAG_MAX_LENGTH], | |
1c79356b A |
294 | int *attributeCount, |
295 | char attributes[TAG_MAX_ATTRIBUTES][TAG_MAX_LENGTH], | |
296 | char values[TAG_MAX_ATTRIBUTES][TAG_MAX_LENGTH] ) | |
297 | { | |
55e303ae | 298 | int length = 0; |
1c79356b A |
299 | int c = currentChar(); |
300 | int tagType = TAG_START; | |
301 | ||
302 | *attributeCount = 0; | |
303 | ||
304 | if (c != '<') return TAG_BAD; | |
305 | c = nextChar(); // skip '<' | |
306 | ||
307 | if (c == '?' || c == '!') { | |
308 | while ((c = nextChar()) != 0) { | |
55e303ae | 309 | if (c == '\n') state->lineNumber++; |
1c79356b A |
310 | if (c == '>') { |
311 | (void)nextChar(); | |
312 | return TAG_COMMENT; | |
313 | } | |
314 | } | |
315 | } | |
316 | ||
317 | if (c == '/') { | |
318 | c = nextChar(); // skip '/' | |
319 | tagType = TAG_END; | |
320 | } | |
321 | if (!isAlpha(c)) return TAG_BAD; | |
322 | ||
323 | /* find end of tag while copying it */ | |
324 | while (isAlphaNumeric(c)) { | |
325 | tag[length++] = c; | |
326 | c = nextChar(); | |
327 | if (length >= (TAG_MAX_LENGTH - 1)) return TAG_BAD; | |
328 | } | |
329 | ||
330 | tag[length] = 0; | |
331 | ||
55e303ae | 332 | // printf("tag %s, type %d\n", tag, tagType); |
1c79356b A |
333 | |
334 | // look for attributes of the form attribute = "value" ... | |
335 | while ((c != '>') && (c != '/')) { | |
336 | while (isSpace(c)) c = nextChar(); | |
337 | ||
338 | length = 0; | |
339 | while (isAlphaNumeric(c)) { | |
340 | attributes[*attributeCount][length++] = c; | |
341 | if (length >= (TAG_MAX_LENGTH - 1)) return TAG_BAD; | |
342 | c = nextChar(); | |
343 | } | |
344 | attributes[*attributeCount][length] = 0; | |
345 | ||
346 | while (isSpace(c)) c = nextChar(); | |
347 | ||
348 | if (c != '=') return TAG_BAD; | |
349 | c = nextChar(); | |
350 | ||
351 | while (isSpace(c)) c = nextChar(); | |
352 | ||
353 | if (c != '"') return TAG_BAD; | |
354 | c = nextChar(); | |
355 | length = 0; | |
356 | while (c != '"') { | |
357 | values[*attributeCount][length++] = c; | |
358 | if (length >= (TAG_MAX_LENGTH - 1)) return TAG_BAD; | |
359 | c = nextChar(); | |
360 | } | |
361 | values[*attributeCount][length] = 0; | |
362 | ||
363 | c = nextChar(); // skip closing quote | |
364 | ||
55e303ae A |
365 | // printf(" attribute '%s' = '%s', nextchar = '%c'\n", |
366 | // attributes[*attributeCount], values[*attributeCount], c); | |
1c79356b A |
367 | |
368 | (*attributeCount)++; | |
369 | if (*attributeCount >= TAG_MAX_ATTRIBUTES) return TAG_BAD; | |
370 | } | |
371 | ||
372 | if (c == '/') { | |
373 | c = nextChar(); // skip '/' | |
374 | tagType = TAG_EMPTY; | |
375 | } | |
376 | if (c != '>') return TAG_BAD; | |
377 | c = nextChar(); // skip '>' | |
378 | ||
379 | return tagType; | |
380 | } | |
381 | ||
382 | static char * | |
55e303ae | 383 | getString(parser_state_t *state) |
1c79356b A |
384 | { |
385 | int c = currentChar(); | |
55e303ae | 386 | int start, length, i, j; |
1c79356b A |
387 | char * tempString; |
388 | ||
55e303ae | 389 | start = state->parseBufferIndex; |
1c79356b A |
390 | /* find end of string */ |
391 | ||
392 | while (c != 0) { | |
55e303ae | 393 | if (c == '\n') state->lineNumber++; |
1c79356b A |
394 | if (c == '<') { |
395 | break; | |
396 | } | |
397 | c = nextChar(); | |
398 | } | |
399 | ||
400 | if (c != '<') return 0; | |
401 | ||
55e303ae | 402 | length = state->parseBufferIndex - start; |
1c79356b A |
403 | |
404 | /* copy to null terminated buffer */ | |
405 | tempString = (char *)malloc(length + 1); | |
406 | if (tempString == 0) { | |
407 | printf("OSUnserializeXML: can't alloc temp memory\n"); | |
55e303ae | 408 | goto error; |
1c79356b A |
409 | } |
410 | ||
411 | // copy out string in tempString | |
412 | // "&" -> '&', "<" -> '<', ">" -> '>' | |
413 | ||
414 | i = j = 0; | |
415 | while (i < length) { | |
55e303ae | 416 | c = state->parseBuffer[start + i++]; |
1c79356b A |
417 | if (c != '&') { |
418 | tempString[j++] = c; | |
419 | } else { | |
420 | if ((i+3) > length) goto error; | |
55e303ae | 421 | c = state->parseBuffer[start + i++]; |
1c79356b | 422 | if (c == 'l') { |
55e303ae A |
423 | if (state->parseBuffer[start + i++] != 't') goto error; |
424 | if (state->parseBuffer[start + i++] != ';') goto error; | |
1c79356b A |
425 | tempString[j++] = '<'; |
426 | continue; | |
427 | } | |
428 | if (c == 'g') { | |
55e303ae A |
429 | if (state->parseBuffer[start + i++] != 't') goto error; |
430 | if (state->parseBuffer[start + i++] != ';') goto error; | |
1c79356b A |
431 | tempString[j++] = '>'; |
432 | continue; | |
433 | } | |
434 | if ((i+3) > length) goto error; | |
435 | if (c == 'a') { | |
55e303ae A |
436 | if (state->parseBuffer[start + i++] != 'm') goto error; |
437 | if (state->parseBuffer[start + i++] != 'p') goto error; | |
438 | if (state->parseBuffer[start + i++] != ';') goto error; | |
1c79356b A |
439 | tempString[j++] = '&'; |
440 | continue; | |
441 | } | |
442 | goto error; | |
443 | } | |
444 | } | |
445 | tempString[j] = 0; | |
446 | ||
55e303ae | 447 | // printf("string %s\n", tempString); |
1c79356b A |
448 | |
449 | return tempString; | |
450 | ||
451 | error: | |
452 | if (tempString) free(tempString); | |
453 | return 0; | |
454 | } | |
455 | ||
456 | static long long | |
55e303ae | 457 | getNumber(parser_state_t *state) |
1c79356b A |
458 | { |
459 | unsigned long long n = 0; | |
460 | int base = 10; | |
91447636 | 461 | bool negate = false; |
1c79356b A |
462 | int c = currentChar(); |
463 | ||
1c79356b A |
464 | if (c == '0') { |
465 | c = nextChar(); | |
466 | if (c == 'x') { | |
467 | base = 16; | |
468 | c = nextChar(); | |
469 | } | |
470 | } | |
471 | if (base == 10) { | |
91447636 A |
472 | if (c == '-') { |
473 | negate = true; | |
474 | c = nextChar(); | |
475 | } | |
1c79356b A |
476 | while(isDigit(c)) { |
477 | n = (n * base + c - '0'); | |
478 | c = nextChar(); | |
479 | } | |
91447636 A |
480 | if (negate) { |
481 | n = (unsigned long long)((long long)n * (long long)-1); | |
482 | } | |
1c79356b A |
483 | } else { |
484 | while(isHexDigit(c)) { | |
485 | if (isDigit(c)) { | |
486 | n = (n * base + c - '0'); | |
487 | } else { | |
488 | n = (n * base + 0xa + c - 'a'); | |
489 | } | |
490 | c = nextChar(); | |
491 | } | |
492 | } | |
55e303ae | 493 | // printf("number 0x%x\n", (unsigned long)n); |
1c79356b A |
494 | return n; |
495 | } | |
496 | ||
497 | // taken from CFXMLParsing/CFPropertyList.c | |
498 | ||
499 | static const signed char __CFPLDataDecodeTable[128] = { | |
500 | /* 000 */ -1, -1, -1, -1, -1, -1, -1, -1, | |
501 | /* 010 */ -1, -1, -1, -1, -1, -1, -1, -1, | |
502 | /* 020 */ -1, -1, -1, -1, -1, -1, -1, -1, | |
503 | /* 030 */ -1, -1, -1, -1, -1, -1, -1, -1, | |
504 | /* ' ' */ -1, -1, -1, -1, -1, -1, -1, -1, | |
505 | /* '(' */ -1, -1, -1, 62, -1, -1, -1, 63, | |
506 | /* '0' */ 52, 53, 54, 55, 56, 57, 58, 59, | |
507 | /* '8' */ 60, 61, -1, -1, -1, 0, -1, -1, | |
508 | /* '@' */ -1, 0, 1, 2, 3, 4, 5, 6, | |
509 | /* 'H' */ 7, 8, 9, 10, 11, 12, 13, 14, | |
510 | /* 'P' */ 15, 16, 17, 18, 19, 20, 21, 22, | |
511 | /* 'X' */ 23, 24, 25, -1, -1, -1, -1, -1, | |
512 | /* '`' */ -1, 26, 27, 28, 29, 30, 31, 32, | |
513 | /* 'h' */ 33, 34, 35, 36, 37, 38, 39, 40, | |
514 | /* 'p' */ 41, 42, 43, 44, 45, 46, 47, 48, | |
515 | /* 'x' */ 49, 50, 51, -1, -1, -1, -1, -1 | |
516 | }; | |
517 | ||
55e303ae | 518 | #define DATA_ALLOC_SIZE 4096 |
1c79356b A |
519 | |
520 | static void * | |
55e303ae | 521 | getCFEncodedData(parser_state_t *state, unsigned int *size) |
1c79356b A |
522 | { |
523 | int numeq = 0, acc = 0, cntr = 0; | |
524 | int tmpbufpos = 0, tmpbuflen = 0; | |
55e303ae | 525 | unsigned char *tmpbuf = (unsigned char *)malloc(DATA_ALLOC_SIZE); |
1c79356b A |
526 | |
527 | int c = currentChar(); | |
528 | *size = 0; | |
529 | ||
530 | while (c != '<') { | |
531 | c &= 0x7f; | |
532 | if (c == 0) { | |
533 | free(tmpbuf); | |
534 | return 0; | |
535 | } | |
536 | if (c == '=') numeq++; else numeq = 0; | |
55e303ae | 537 | if (c == '\n') state->lineNumber++; |
1c79356b A |
538 | if (__CFPLDataDecodeTable[c] < 0) { |
539 | c = nextChar(); | |
540 | continue; | |
541 | } | |
542 | cntr++; | |
543 | acc <<= 6; | |
544 | acc += __CFPLDataDecodeTable[c]; | |
545 | if (0 == (cntr & 0x3)) { | |
546 | if (tmpbuflen <= tmpbufpos + 2) { | |
55e303ae | 547 | tmpbuflen += DATA_ALLOC_SIZE; |
1c79356b A |
548 | tmpbuf = (unsigned char *)realloc(tmpbuf, tmpbuflen); |
549 | } | |
550 | tmpbuf[tmpbufpos++] = (acc >> 16) & 0xff; | |
551 | if (numeq < 2) | |
552 | tmpbuf[tmpbufpos++] = (acc >> 8) & 0xff; | |
553 | if (numeq < 1) | |
554 | tmpbuf[tmpbufpos++] = acc & 0xff; | |
555 | } | |
556 | c = nextChar(); | |
557 | } | |
558 | *size = tmpbufpos; | |
55e303ae A |
559 | if (*size == 0) { |
560 | free(tmpbuf); | |
561 | return 0; | |
562 | } | |
1c79356b A |
563 | return tmpbuf; |
564 | } | |
565 | ||
566 | static void * | |
55e303ae | 567 | getHexData(parser_state_t *state, unsigned int *size) |
1c79356b A |
568 | { |
569 | int c; | |
570 | unsigned char *d, *start, *lastStart; | |
571 | ||
55e303ae | 572 | start = lastStart = d = (unsigned char *)malloc(DATA_ALLOC_SIZE); |
1c79356b A |
573 | c = currentChar(); |
574 | ||
575 | while (c != '<') { | |
576 | ||
577 | if (isSpace(c)) while ((c = nextChar()) != 0 && isSpace(c)) {}; | |
578 | if (c == '\n') { | |
55e303ae | 579 | state->lineNumber++; |
1c79356b A |
580 | c = nextChar(); |
581 | continue; | |
582 | } | |
583 | ||
584 | // get high nibble | |
585 | if (isDigit(c)) { | |
586 | *d = (c - '0') << 4; | |
587 | } else if (isAlphaDigit(c)) { | |
588 | *d = (0xa + (c - 'a')) << 4; | |
589 | } else { | |
590 | goto error; | |
591 | } | |
592 | ||
593 | // get low nibble | |
594 | c = nextChar(); | |
595 | if (isDigit(c)) { | |
596 | *d |= c - '0'; | |
597 | } else if (isAlphaDigit(c)) { | |
598 | *d |= 0xa + (c - 'a'); | |
599 | } else { | |
600 | goto error; | |
601 | } | |
602 | ||
603 | d++; | |
55e303ae | 604 | if ((d - lastStart) >= DATA_ALLOC_SIZE) { |
1c79356b | 605 | int oldsize = d - start; |
55e303ae | 606 | start = (unsigned char *)realloc(start, oldsize + DATA_ALLOC_SIZE); |
1c79356b A |
607 | d = lastStart = start + oldsize; |
608 | } | |
609 | c = nextChar(); | |
610 | } | |
611 | ||
612 | *size = d - start; | |
613 | return start; | |
614 | ||
615 | error: | |
616 | ||
617 | *size = 0; | |
618 | free(start); | |
619 | return 0; | |
620 | } | |
621 | ||
622 | static int | |
55e303ae | 623 | yylex(YYSTYPE *lvalp, parser_state_t *state) |
1c79356b | 624 | { |
55e303ae | 625 | int c, i; |
1c79356b A |
626 | int tagType; |
627 | char tag[TAG_MAX_LENGTH]; | |
628 | int attributeCount; | |
629 | char attributes[TAG_MAX_ATTRIBUTES][TAG_MAX_LENGTH]; | |
630 | char values[TAG_MAX_ATTRIBUTES][TAG_MAX_LENGTH]; | |
55e303ae | 631 | object_t *object; |
1c79356b A |
632 | |
633 | top: | |
634 | c = currentChar(); | |
635 | ||
636 | /* skip white space */ | |
637 | if (isSpace(c)) while ((c = nextChar()) != 0 && isSpace(c)) {}; | |
638 | ||
639 | /* keep track of line number, don't return \n's */ | |
640 | if (c == '\n') { | |
55e303ae | 641 | STATE->lineNumber++; |
1c79356b A |
642 | (void)nextChar(); |
643 | goto top; | |
644 | } | |
1c79356b | 645 | |
55e303ae A |
646 | // end of the buffer? |
647 | if (!c) return 0; | |
648 | ||
649 | tagType = getTag(STATE, tag, &attributeCount, attributes, values); | |
1c79356b A |
650 | if (tagType == TAG_BAD) return SYNTAX_ERROR; |
651 | if (tagType == TAG_COMMENT) goto top; | |
652 | ||
653 | // handle allocation and check for "ID" and "IDREF" tags up front | |
55e303ae A |
654 | *lvalp = object = newObject(STATE); |
655 | object->idref = -1; | |
656 | for (i=0; i < attributeCount; i++) { | |
1c79356b A |
657 | if (attributes[i][0] == 'I' && attributes[i][1] == 'D') { |
658 | // check for idref's, note: we ignore the tag, for | |
659 | // this to work correctly, all idrefs must be unique | |
660 | // across the whole serialization | |
661 | if (attributes[i][2] == 'R' && attributes[i][3] == 'E' && | |
662 | attributes[i][4] == 'F' && !attributes[i][5]) { | |
663 | if (tagType != TAG_EMPTY) return SYNTAX_ERROR; | |
55e303ae | 664 | object->idref = strtol(values[i], NULL, 0); |
1c79356b A |
665 | return IDREF; |
666 | } | |
667 | // check for id's | |
668 | if (!attributes[i][2]) { | |
55e303ae | 669 | object->idref = strtol(values[i], NULL, 0); |
1c79356b A |
670 | } else { |
671 | return SYNTAX_ERROR; | |
672 | } | |
673 | } | |
674 | } | |
675 | ||
676 | switch (*tag) { | |
677 | case 'a': | |
678 | if (!strcmp(tag, "array")) { | |
679 | if (tagType == TAG_EMPTY) { | |
55e303ae | 680 | object->elements = NULL; |
1c79356b A |
681 | return ARRAY; |
682 | } | |
683 | return (tagType == TAG_START) ? '(' : ')'; | |
684 | } | |
685 | break; | |
686 | case 'd': | |
687 | if (!strcmp(tag, "dict")) { | |
688 | if (tagType == TAG_EMPTY) { | |
55e303ae | 689 | object->elements = NULL; |
1c79356b A |
690 | return DICTIONARY; |
691 | } | |
692 | return (tagType == TAG_START) ? '{' : '}'; | |
693 | } | |
694 | if (!strcmp(tag, "data")) { | |
695 | unsigned int size; | |
1c79356b | 696 | if (tagType == TAG_EMPTY) { |
55e303ae A |
697 | object->data = NULL; |
698 | object->size = 0; | |
1c79356b A |
699 | return DATA; |
700 | } | |
55e303ae A |
701 | |
702 | bool isHexFormat = false; | |
1c79356b A |
703 | for (int i=0; i < attributeCount; i++) { |
704 | if (!strcmp(attributes[i], "format") && !strcmp(values[i], "hex")) { | |
55e303ae | 705 | isHexFormat = true; |
1c79356b A |
706 | break; |
707 | } | |
708 | } | |
709 | // CF encoded is the default form | |
55e303ae A |
710 | if (isHexFormat) { |
711 | object->data = getHexData(STATE, &size); | |
1c79356b | 712 | } else { |
55e303ae | 713 | object->data = getCFEncodedData(STATE, &size); |
1c79356b | 714 | } |
55e303ae A |
715 | object->size = size; |
716 | if ((getTag(STATE, tag, &attributeCount, attributes, values) != TAG_END) || strcmp(tag, "data")) { | |
1c79356b A |
717 | return SYNTAX_ERROR; |
718 | } | |
719 | return DATA; | |
720 | } | |
721 | break; | |
722 | case 'f': | |
723 | if (!strcmp(tag, "false")) { | |
724 | if (tagType == TAG_EMPTY) { | |
55e303ae | 725 | object->number = 0; |
1c79356b A |
726 | return BOOLEAN; |
727 | } | |
728 | } | |
729 | break; | |
730 | case 'i': | |
731 | if (!strcmp(tag, "integer")) { | |
55e303ae A |
732 | object->size = 64; // default |
733 | for (i=0; i < attributeCount; i++) { | |
1c79356b | 734 | if (!strcmp(attributes[i], "size")) { |
55e303ae | 735 | object->size = strtoul(values[i], NULL, 0); |
1c79356b A |
736 | } |
737 | } | |
738 | if (tagType == TAG_EMPTY) { | |
55e303ae | 739 | object->number = 0; |
1c79356b A |
740 | return NUMBER; |
741 | } | |
55e303ae A |
742 | object->number = getNumber(STATE); |
743 | if ((getTag(STATE, tag, &attributeCount, attributes, values) != TAG_END) || strcmp(tag, "integer")) { | |
1c79356b A |
744 | return SYNTAX_ERROR; |
745 | } | |
746 | return NUMBER; | |
747 | } | |
748 | break; | |
749 | case 'k': | |
750 | if (!strcmp(tag, "key")) { | |
751 | if (tagType == TAG_EMPTY) return SYNTAX_ERROR; | |
55e303ae A |
752 | object->string = getString(STATE); |
753 | if (!object->string) { | |
1c79356b A |
754 | return SYNTAX_ERROR; |
755 | } | |
55e303ae | 756 | if ((getTag(STATE, tag, &attributeCount, attributes, values) != TAG_END) |
1c79356b A |
757 | || strcmp(tag, "key")) { |
758 | return SYNTAX_ERROR; | |
759 | } | |
760 | return KEY; | |
761 | } | |
762 | break; | |
763 | case 'p': | |
764 | if (!strcmp(tag, "plist")) { | |
55e303ae | 765 | freeObject(STATE, object); |
1c79356b A |
766 | goto top; |
767 | } | |
768 | break; | |
769 | case 's': | |
770 | if (!strcmp(tag, "string")) { | |
771 | if (tagType == TAG_EMPTY) { | |
55e303ae A |
772 | object->string = (char *)malloc(1); |
773 | object->string[0] = 0; | |
1c79356b A |
774 | return STRING; |
775 | } | |
55e303ae A |
776 | object->string = getString(STATE); |
777 | if (!object->string) { | |
1c79356b A |
778 | return SYNTAX_ERROR; |
779 | } | |
55e303ae | 780 | if ((getTag(STATE, tag, &attributeCount, attributes, values) != TAG_END) |
1c79356b A |
781 | || strcmp(tag, "string")) { |
782 | return SYNTAX_ERROR; | |
783 | } | |
784 | return STRING; | |
785 | } | |
786 | if (!strcmp(tag, "set")) { | |
787 | if (tagType == TAG_EMPTY) { | |
55e303ae | 788 | object->elements = NULL; |
1c79356b A |
789 | return SET;; |
790 | } | |
791 | if (tagType == TAG_START) { | |
792 | return '['; | |
793 | } else { | |
794 | return ']'; | |
795 | } | |
796 | } | |
797 | break; | |
798 | case 't': | |
799 | if (!strcmp(tag, "true")) { | |
800 | if (tagType == TAG_EMPTY) { | |
55e303ae | 801 | object->number = 1; |
1c79356b A |
802 | return BOOLEAN; |
803 | } | |
804 | } | |
805 | break; | |
1c79356b A |
806 | } |
807 | ||
55e303ae | 808 | return SYNTAX_ERROR; |
1c79356b A |
809 | } |
810 | ||
811 | // !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!# | |
812 | // !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!# | |
813 | // !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!# | |
814 | ||
815 | // "java" like allocation, if this code hits a syntax error in the | |
816 | // the middle of the parsed string we just bail with pointers hanging | |
55e303ae | 817 | // all over place, this code helps keeps it all together |
1c79356b | 818 | |
55e303ae | 819 | //static int object_count = 0; |
1c79356b A |
820 | |
821 | object_t * | |
55e303ae | 822 | newObject(parser_state_t *state) |
1c79356b A |
823 | { |
824 | object_t *o; | |
825 | ||
55e303ae A |
826 | if (state->freeObjects) { |
827 | o = state->freeObjects; | |
828 | state->freeObjects = state->freeObjects->next; | |
1c79356b A |
829 | } else { |
830 | o = (object_t *)malloc(sizeof(object_t)); | |
55e303ae | 831 | // object_count++; |
1c79356b | 832 | bzero(o, sizeof(object_t)); |
55e303ae A |
833 | o->free = state->objects; |
834 | state->objects = o; | |
1c79356b A |
835 | } |
836 | ||
837 | return o; | |
838 | } | |
839 | ||
840 | void | |
55e303ae | 841 | freeObject(parser_state_t * state, object_t *o) |
1c79356b | 842 | { |
55e303ae A |
843 | o->next = state->freeObjects; |
844 | state->freeObjects = o; | |
1c79356b A |
845 | } |
846 | ||
847 | void | |
55e303ae | 848 | cleanupObjects(parser_state_t *state) |
1c79356b | 849 | { |
55e303ae | 850 | object_t *t, *o = state->objects; |
1c79356b A |
851 | |
852 | while (o) { | |
853 | if (o->object) { | |
55e303ae | 854 | // printf("OSUnserializeXML: releasing object o=%x object=%x\n", (int)o, (int)o->object); |
1c79356b A |
855 | o->object->release(); |
856 | } | |
857 | if (o->data) { | |
55e303ae | 858 | // printf("OSUnserializeXML: freeing object o=%x data=%x\n", (int)o, (int)o->data); |
1c79356b A |
859 | free(o->data); |
860 | } | |
861 | if (o->key) { | |
55e303ae | 862 | // printf("OSUnserializeXML: releasing object o=%x key=%x\n", (int)o, (int)o->key); |
1c79356b A |
863 | o->key->release(); |
864 | } | |
865 | if (o->string) { | |
55e303ae | 866 | // printf("OSUnserializeXML: freeing object o=%x string=%x\n", (int)o, (int)o->string); |
1c79356b A |
867 | free(o->string); |
868 | } | |
869 | ||
870 | t = o; | |
871 | o = o->free; | |
872 | free(t); | |
55e303ae | 873 | // object_count--; |
1c79356b | 874 | } |
55e303ae | 875 | // printf("object_count = %d\n", object_count); |
1c79356b A |
876 | } |
877 | ||
878 | // !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!# | |
879 | // !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!# | |
880 | // !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!# | |
881 | ||
1c79356b | 882 | static void |
55e303ae | 883 | rememberObject(parser_state_t *state, int tag, OSObject *o) |
1c79356b A |
884 | { |
885 | char key[16]; | |
55e303ae | 886 | snprintf(key, 16, "%u", tag); |
1c79356b | 887 | |
55e303ae | 888 | // printf("remember key %s\n", key); |
1c79356b | 889 | |
55e303ae | 890 | state->tags->setObject(key, o); |
1c79356b A |
891 | } |
892 | ||
893 | static object_t * | |
55e303ae | 894 | retrieveObject(parser_state_t *state, int tag) |
1c79356b | 895 | { |
55e303ae A |
896 | OSObject *ref; |
897 | object_t *o; | |
1c79356b | 898 | char key[16]; |
55e303ae | 899 | snprintf(key, 16, "%u", tag); |
1c79356b | 900 | |
55e303ae | 901 | // printf("retrieve key '%s'\n", key); |
1c79356b | 902 | |
55e303ae | 903 | ref = state->tags->getObject(key); |
1c79356b A |
904 | if (!ref) return 0; |
905 | ||
55e303ae | 906 | o = newObject(state); |
1c79356b A |
907 | o->object = ref; |
908 | return o; | |
909 | } | |
910 | ||
911 | // !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!# | |
912 | // !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!# | |
913 | // !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!# | |
914 | ||
915 | object_t * | |
55e303ae | 916 | buildDictionary(parser_state_t *state, object_t * header) |
1c79356b A |
917 | { |
918 | object_t *o, *t; | |
919 | int count = 0; | |
55e303ae | 920 | OSDictionary *dict; |
1c79356b A |
921 | |
922 | // get count and reverse order | |
923 | o = header->elements; | |
924 | header->elements = 0; | |
925 | while (o) { | |
926 | count++; | |
927 | t = o; | |
928 | o = o->next; | |
929 | ||
930 | t->next = header->elements; | |
931 | header->elements = t; | |
932 | } | |
933 | ||
55e303ae A |
934 | dict = OSDictionary::withCapacity(count); |
935 | if (header->idref >= 0) rememberObject(state, header->idref, dict); | |
1c79356b A |
936 | |
937 | o = header->elements; | |
938 | while (o) { | |
55e303ae A |
939 | dict->setObject(o->key, o->object); |
940 | ||
1c79356b | 941 | o->key->release(); |
55e303ae | 942 | o->object->release(); |
1c79356b | 943 | o->key = 0; |
55e303ae A |
944 | o->object = 0; |
945 | ||
1c79356b A |
946 | t = o; |
947 | o = o->next; | |
55e303ae | 948 | freeObject(state, t); |
1c79356b A |
949 | } |
950 | o = header; | |
55e303ae | 951 | o->object = dict; |
1c79356b A |
952 | return o; |
953 | }; | |
954 | ||
955 | object_t * | |
55e303ae | 956 | buildArray(parser_state_t *state, object_t * header) |
1c79356b A |
957 | { |
958 | object_t *o, *t; | |
959 | int count = 0; | |
55e303ae | 960 | OSArray *array; |
1c79356b A |
961 | |
962 | // get count and reverse order | |
963 | o = header->elements; | |
964 | header->elements = 0; | |
965 | while (o) { | |
966 | count++; | |
967 | t = o; | |
968 | o = o->next; | |
969 | ||
970 | t->next = header->elements; | |
971 | header->elements = t; | |
972 | } | |
973 | ||
55e303ae A |
974 | array = OSArray::withCapacity(count); |
975 | if (header->idref >= 0) rememberObject(state, header->idref, array); | |
1c79356b A |
976 | |
977 | o = header->elements; | |
978 | while (o) { | |
55e303ae A |
979 | array->setObject(o->object); |
980 | ||
1c79356b A |
981 | o->object->release(); |
982 | o->object = 0; | |
55e303ae | 983 | |
1c79356b A |
984 | t = o; |
985 | o = o->next; | |
55e303ae | 986 | freeObject(state, t); |
1c79356b A |
987 | } |
988 | o = header; | |
55e303ae | 989 | o->object = array; |
1c79356b A |
990 | return o; |
991 | }; | |
992 | ||
993 | object_t * | |
55e303ae | 994 | buildSet(parser_state_t *state, object_t *header) |
1c79356b | 995 | { |
55e303ae | 996 | object_t *o = buildArray(state, header); |
1c79356b | 997 | |
55e303ae A |
998 | OSArray *array = (OSArray *)o->object; |
999 | OSSet *set = OSSet::withArray(array, array->getCapacity()); | |
1c79356b | 1000 | |
55e303ae A |
1001 | // write over the reference created in buildArray |
1002 | if (header->idref >= 0) rememberObject(state, header->idref, set); | |
1c79356b | 1003 | |
55e303ae A |
1004 | array->release(); |
1005 | o->object = set; | |
1c79356b A |
1006 | return o; |
1007 | }; | |
1008 | ||
1009 | object_t * | |
55e303ae | 1010 | buildString(parser_state_t *state, object_t *o) |
1c79356b | 1011 | { |
55e303ae | 1012 | OSString *string; |
1c79356b | 1013 | |
55e303ae A |
1014 | string = OSString::withCString(o->string); |
1015 | if (o->idref >= 0) rememberObject(state, o->idref, string); | |
1c79356b A |
1016 | |
1017 | free(o->string); | |
1018 | o->string = 0; | |
55e303ae | 1019 | o->object = string; |
1c79356b A |
1020 | |
1021 | return o; | |
1022 | }; | |
1023 | ||
1024 | object_t * | |
55e303ae | 1025 | buildData(parser_state_t *state, object_t *o) |
1c79356b | 1026 | { |
55e303ae | 1027 | OSData *data; |
1c79356b A |
1028 | |
1029 | if (o->size) { | |
55e303ae | 1030 | data = OSData::withBytes(o->data, o->size); |
1c79356b | 1031 | } else { |
55e303ae | 1032 | data = OSData::withCapacity(0); |
1c79356b | 1033 | } |
55e303ae | 1034 | if (o->idref >= 0) rememberObject(state, o->idref, data); |
1c79356b | 1035 | |
55e303ae | 1036 | if (o->size) free(o->data); |
1c79356b | 1037 | o->data = 0; |
55e303ae | 1038 | o->object = data; |
1c79356b A |
1039 | return o; |
1040 | }; | |
1041 | ||
1042 | object_t * | |
55e303ae | 1043 | buildNumber(parser_state_t *state, object_t *o) |
1c79356b | 1044 | { |
55e303ae | 1045 | OSNumber *number = OSNumber::withNumber(o->number, o->size); |
1c79356b | 1046 | |
55e303ae | 1047 | if (o->idref >= 0) rememberObject(state, o->idref, number); |
1c79356b | 1048 | |
55e303ae | 1049 | o->object = number; |
1c79356b A |
1050 | return o; |
1051 | }; | |
1052 | ||
1053 | object_t * | |
55e303ae | 1054 | buildBoolean(parser_state_t *state, object_t *o) |
1c79356b | 1055 | { |
55e303ae A |
1056 | o->object = ((o->number == 0) ? kOSBooleanFalse : kOSBooleanTrue); |
1057 | o->object->retain(); | |
1c79356b A |
1058 | return o; |
1059 | }; | |
1060 | ||
1c79356b A |
1061 | OSObject* |
1062 | OSUnserializeXML(const char *buffer, OSString **errorString) | |
1063 | { | |
1064 | OSObject *object; | |
55e303ae | 1065 | parser_state_t *state = (parser_state_t *)malloc(sizeof(parser_state_t)); |
1c79356b | 1066 | |
55e303ae | 1067 | if ((!state) || (!buffer)) return 0; |
1c79356b | 1068 | |
55e303ae A |
1069 | // just in case |
1070 | if (errorString) *errorString = NULL; | |
1c79356b | 1071 | |
55e303ae A |
1072 | state->parseBuffer = buffer; |
1073 | state->parseBufferIndex = 0; | |
1074 | state->lineNumber = 1; | |
1075 | state->objects = 0; | |
1076 | state->freeObjects = 0; | |
1077 | state->tags = OSDictionary::withCapacity(128); | |
1078 | state->errorString = errorString; | |
1079 | state->parsedObject = 0; | |
1080 | ||
1081 | (void)yyparse((void *)state); | |
1082 | ||
1083 | object = state->parsedObject; | |
1c79356b | 1084 | |
55e303ae A |
1085 | cleanupObjects(state); |
1086 | state->tags->release(); | |
1087 | free(state); | |
1c79356b A |
1088 | |
1089 | return object; | |
1090 | } | |
1091 | ||
1092 | ||
1093 | // | |
1094 | // | |
1095 | // | |
1096 | // | |
1097 | // | |
1098 | // DO NOT EDIT OSUnserializeXML.cpp! | |
1099 | // | |
1100 | // this means you! | |
1101 | // | |
1102 | // | |
1103 | // | |
1104 | // | |
1105 | // |