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