]>
Commit | Line | Data |
---|---|---|
8be739c0 A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
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 | /* | |
23 | * plist.c - plist parsing functions | |
24 | * | |
25 | * Copyright (c) 2000-2005 Apple Computer, Inc. | |
26 | * | |
27 | * DRI: Josh de Cesare | |
28 | * code split out from drivers.c by Soren Spies, 2005 | |
29 | */ | |
30 | ||
31 | #include <sl.h> | |
32 | ||
873b6fa6 A |
33 | //#define PLIST_DEBUG 1 // whether to report detailed parsing errors |
34 | ||
8be739c0 A |
35 | #define kXMLTagPList "plist" |
36 | #define kXMLTagDict "dict" | |
37 | #define kXMLTagKey "key" | |
38 | #define kXMLTagString "string" | |
39 | #define kXMLTagInteger "integer" | |
40 | #define kXMLTagData "data" | |
41 | #define kXMLTagDate "date" | |
42 | #define kXMLTagFalse "false/" | |
43 | #define kXMLTagTrue "true/" | |
44 | #define kXMLTagArray "array" | |
45 | // for back-references used by libkern serializer | |
46 | #define kXMLTagReference "reference" | |
47 | #define kXMLTagID "ID=" | |
48 | #define kXMLTagIDREF "IDREF=" | |
49 | ||
50 | static long ParseNextTag(char *buffer, TagPtr *tag); | |
51 | static long ParseTagList(char *buffer, TagPtr *tag, long type, long empty); | |
52 | static long ParseTagKey(char *buffer, TagPtr *tag); | |
53 | static long ParseTagString(char *buffer, TagPtr *tag); | |
54 | static long ParseTagInteger(char *buffer, TagPtr *tag); | |
55 | static long ParseTagData(char *buffer, TagPtr *tag); | |
56 | static long ParseTagDate(char *buffer, TagPtr *tag); | |
57 | static long ParseTagBoolean(char *buffer, TagPtr *tag, long type); | |
58 | static long GetNextTag(char *buffer, char **tag, long *start, long *empty); | |
59 | static long FixDataMatchingTag(char *buffer, char *tag); | |
60 | static TagPtr NewTag(void); | |
61 | static char *NewSymbol(char *string); | |
62 | static void FreeSymbol(char *string); | |
63 | ||
64 | #if PLIST_DEBUG | |
65 | // for debugging parsing failures | |
66 | static int gTagsParsed; | |
67 | static char *gLastTag; | |
68 | #endif | |
69 | ||
70 | TagPtr GetProperty(TagPtr dict, char *key) | |
71 | { | |
72 | TagPtr tagList, tag; | |
73 | ||
74 | if (dict->type != kTagTypeDict) return 0; | |
75 | ||
76 | tag = 0; // ? | |
77 | tagList = dict->tag; | |
78 | while (tagList) { | |
79 | tag = tagList; | |
80 | tagList = tag->tagNext; | |
81 | ||
82 | if ((tag->type != kTagTypeKey) || (tag->string == 0)) continue; | |
83 | ||
84 | if (!strcmp(tag->string, key)) { | |
85 | return tag->tag; | |
86 | } | |
87 | } | |
88 | ||
89 | return 0; | |
90 | } | |
91 | ||
92 | // intended to look for two versions of the tag; now just for sizeof | |
93 | #define MATCHTAG(parsedTag, keyTag) \ | |
94 | (!strncmp(parsedTag, keyTag, sizeof(keyTag)-1)) | |
95 | ||
96 | // a tag cache for iokit's super-plists (alas, to merge w/"Symbol" cache?) | |
97 | // we're not going to use the 0th element; it's used by the whole dict anyway | |
98 | static int lastid; | |
99 | static int numids = 0; // skipping 0th | |
100 | static TagPtr *idtags; | |
101 | ||
102 | #define INITIALIDS 10 | |
103 | static long InitTagCache() | |
104 | { | |
105 | long rval = -1; | |
106 | ||
107 | do { | |
108 | lastid = 0; | |
109 | numids = INITIALIDS; | |
110 | idtags = (TagPtr*)malloc(numids * sizeof(*idtags)); | |
111 | if (!idtags) break; | |
112 | bzero(idtags, numids * sizeof(*idtags)); | |
113 | ||
114 | rval = 0; | |
115 | } while(0); | |
116 | ||
117 | return rval; | |
118 | } | |
119 | ||
120 | static void FreeTagCache() | |
121 | { | |
122 | if (idtags) | |
123 | free(idtags); | |
124 | idtags = NULL; | |
125 | } | |
126 | ||
127 | // get the number from (e.g.): ID="3" | |
128 | static int ExtractID(char *tagName, int tagLen) | |
129 | { | |
130 | char *idptr = tagName + tagLen; | |
131 | int rval = 0; | |
132 | ||
133 | while(*idptr != '>') { | |
134 | idptr++; | |
135 | if (MATCHTAG(idptr, kXMLTagID)) { | |
136 | rval = strtol(idptr + sizeof(kXMLTagID)-1+1, 0, 0); // -NUL +" | |
137 | break; | |
138 | } else if (MATCHTAG(idptr, kXMLTagIDREF)) { | |
139 | rval = strtol(idptr + sizeof(kXMLTagIDREF)-1+1, 0, 0); // -NUL +" | |
140 | break; | |
141 | } | |
142 | // there can be multiple modifiers (integers have 'size' first) | |
143 | while(*idptr != ' ' && *idptr != '>') idptr++; | |
144 | } | |
145 | ||
146 | return rval; | |
147 | } | |
148 | ||
149 | static TagPtr TagFromRef(char *tagName, int tagLen) | |
150 | { | |
151 | int refidx = ExtractID(tagName, tagLen); | |
152 | TagPtr rval = NULL; | |
153 | ||
154 | if (refidx <= lastid) | |
155 | rval = idtags[refidx]; | |
156 | ||
157 | return rval; | |
158 | } | |
159 | ||
160 | static long SaveTagRef(TagPtr tag, int tagid) | |
161 | { | |
162 | ||
163 | // bumped any time we skip an unsupported tag | |
164 | if (tagid != ++lastid) { | |
165 | if (tagid > lastid) { | |
166 | lastid = tagid; | |
167 | } | |
168 | else { | |
169 | printf("invalid plist: tagid (%d) < lastid (%d)??\n", tagid, lastid); | |
170 | return -1; | |
171 | } | |
172 | } | |
173 | ||
174 | // upsize idtags if needed | |
175 | if (numids <= lastid) { | |
176 | while(numids <= lastid) | |
177 | numids *= 2; | |
178 | idtags = (TagPtr*)realloc(idtags, numids * sizeof(*idtags)); | |
179 | if (!idtags) return -1; | |
180 | } | |
181 | ||
182 | // and record for later | |
183 | idtags[lastid] = tag; | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
188 | long ParseXML(char *buffer, TagPtr *dict) | |
189 | { | |
190 | long length, pos; | |
191 | TagPtr moduleDict; | |
192 | ||
193 | #if PLIST_DEBUG | |
194 | gTagsParsed = 0; | |
195 | gLastTag = NULL; | |
196 | #endif | |
197 | if (InitTagCache()) return -1; | |
198 | pos = 0; | |
199 | while (1) { | |
200 | moduleDict = (TagPtr)-1; // have to detect changes to by-ref parameter | |
201 | length = ParseNextTag(buffer + pos, &moduleDict); | |
202 | if (length == -1) break; | |
203 | pos += length; | |
204 | ||
205 | if (moduleDict == 0) continue; | |
206 | ||
207 | // did we actually create anything? | |
208 | if (moduleDict != (TagPtr)-1) { | |
209 | if (moduleDict->type == kTagTypeDict) break; | |
210 | if (moduleDict->type == kTagTypeArray) break; | |
211 | ||
212 | FreeTag(moduleDict); | |
213 | } | |
214 | } | |
215 | ||
8be739c0 | 216 | |
873b6fa6 A |
217 | if (length != -1) { |
218 | *dict = moduleDict; | |
8be739c0 | 219 | #if PLIST_DEBUG |
873b6fa6 A |
220 | } else { |
221 | printf("ParseXML gagged (-1) after '%s' (%d tags); buf+pos: %s\n", | |
8be739c0 A |
222 | gLastTag,gTagsParsed,buffer+pos); |
223 | #endif | |
873b6fa6 | 224 | } |
8be739c0 A |
225 | |
226 | // for tidyness even though kext parsing resets all of malloc | |
227 | FreeTagCache(); | |
228 | ||
229 | // return 0 for no error | |
230 | return (length != -1) ? 0 : -1; | |
231 | } | |
232 | ||
233 | #define PARSESTASHINGTAG(tagBuf, keyTag, parseFunc) do { \ | |
234 | TagPtr extantTag = TagFromRef(tagBuf, sizeof(keyTag)-1); \ | |
235 | if (extantTag) { \ | |
236 | *tag = extantTag; \ | |
237 | length = 0; \ | |
238 | } else { \ | |
239 | int tagid = ExtractID(tagName, sizeof(keyTag)-1); \ | |
240 | length = parseFunc(buffer + pos, tag); \ | |
241 | if (tagid && length != -1) \ | |
242 | if (-1 == SaveTagRef(*tag, tagid)) \ | |
243 | return -1; \ | |
244 | } \ | |
245 | } while(0) | |
246 | ||
247 | static long ParseNextTag(char *buffer, TagPtr *tag) | |
248 | { | |
249 | long length, pos, empty = 0; | |
250 | char *tagName; | |
251 | TagPtr refTag; | |
252 | ||
253 | length = GetNextTag(buffer, &tagName, 0, &empty); | |
254 | if (length == -1) return -1; | |
255 | #if PLIST_DEBUG | |
256 | gLastTag = tagName; | |
257 | gTagsParsed++; | |
258 | #endif | |
259 | ||
260 | pos = length; | |
261 | if (MATCHTAG(tagName, kXMLTagPList)) { | |
262 | length = 0; // just a header; nothing to parse | |
263 | // return-via-reference tag should be left alone | |
264 | } else if (MATCHTAG(tagName, kXMLTagDict)) { | |
265 | length = ParseTagList(buffer + pos, tag, kTagTypeDict, empty); | |
266 | } else if (!strcmp(tagName, kXMLTagKey)) { | |
267 | length = ParseTagKey(buffer + pos, tag); | |
268 | } else if (MATCHTAG(tagName, kXMLTagReference) && | |
269 | (refTag = TagFromRef(tagName, sizeof(kXMLTagReference)-1))) { | |
270 | *tag = refTag; | |
271 | length = 0; | |
272 | } else if (MATCHTAG(tagName, kXMLTagString)) { | |
273 | PARSESTASHINGTAG(tagName, kXMLTagString, ParseTagString); | |
274 | } else if (MATCHTAG(tagName, kXMLTagInteger)) { | |
275 | PARSESTASHINGTAG(tagName, kXMLTagInteger, ParseTagInteger); | |
276 | } else if (!strcmp(tagName, kXMLTagData)) { | |
277 | length = ParseTagData(buffer + pos, tag); | |
278 | } else if (!strcmp(tagName, kXMLTagDate)) { | |
279 | length = ParseTagDate(buffer + pos, tag); | |
280 | } else if (!strcmp(tagName, kXMLTagFalse)) { | |
281 | length = ParseTagBoolean(buffer + pos, tag, kTagTypeFalse); | |
282 | } else if (!strcmp(tagName, kXMLTagTrue)) { | |
283 | length = ParseTagBoolean(buffer + pos, tag, kTagTypeTrue); | |
284 | } else if (MATCHTAG(tagName, kXMLTagArray)) { | |
285 | length = ParseTagList(buffer + pos, tag, kTagTypeArray, empty); | |
286 | } else { | |
287 | // it wasn't parsed so we consumed no additional characters | |
288 | length = 0; | |
289 | if (tagName[0] == '/') // was it an end tag (indicated w/*tag = 0) | |
290 | *tag = 0; | |
291 | else { | |
292 | //printf("ignored plist tag: %s (*tag: %x)\n", tagName, *tag); | |
293 | *tag = (TagPtr)-1; // we're *not* returning a tag | |
294 | } | |
295 | } | |
296 | ||
297 | if (length == -1) return -1; | |
298 | ||
299 | return pos + length; | |
300 | } | |
301 | ||
302 | static long ParseTagList(char *buffer, TagPtr *tag, long type, long empty) | |
303 | { | |
304 | long length, pos; | |
305 | TagPtr tagList, tmpTag = (TagPtr)-1; | |
306 | ||
307 | tagList = 0; | |
308 | pos = 0; | |
309 | ||
310 | if (!empty) { | |
311 | while (1) { | |
312 | tmpTag = (TagPtr)-1; | |
313 | length = ParseNextTag(buffer + pos, &tmpTag); | |
314 | if (length == -1) break; | |
315 | pos += length; | |
316 | ||
317 | // detect end of list | |
318 | if (tmpTag == 0) break; | |
319 | ||
320 | // if we made a new tag, insert into list | |
321 | if (tmpTag != (TagPtr)-1) { | |
322 | tmpTag->tagNext = tagList; | |
323 | tagList = tmpTag; | |
324 | } | |
325 | } | |
326 | ||
327 | if (length == -1) { | |
328 | FreeTag(tagList); | |
329 | return -1; | |
330 | } | |
331 | } | |
332 | ||
333 | tmpTag = NewTag(); | |
334 | if (tmpTag == 0) { | |
335 | FreeTag(tagList); | |
336 | return -1; | |
337 | } | |
338 | ||
339 | tmpTag->type = type; | |
340 | tmpTag->string = 0; | |
341 | tmpTag->tag = tagList; | |
342 | tmpTag->tagNext = 0; | |
343 | ||
344 | *tag = tmpTag; | |
345 | ||
346 | return pos; | |
347 | } | |
348 | ||
349 | ||
350 | static long ParseTagKey(char *buffer, TagPtr *tag) | |
351 | { | |
352 | long length, length2; | |
353 | char *string; | |
354 | TagPtr tmpTag, subTag = (TagPtr)-1; // eliminate possible stale tag | |
355 | ||
356 | length = FixDataMatchingTag(buffer, kXMLTagKey); | |
357 | if (length == -1) return -1; | |
358 | ||
359 | length2 = ParseNextTag(buffer + length, &subTag); | |
360 | if (length2 == -1) return -1; | |
361 | ||
362 | // XXXX revisit 4063982 if FreeTag becomes real | |
363 | if(subTag == (TagPtr)-1) | |
364 | subTag = NULL; | |
365 | ||
366 | tmpTag = NewTag(); | |
367 | if (tmpTag == 0) { | |
368 | FreeTag(subTag); | |
369 | return -1; | |
370 | } | |
371 | ||
372 | string = NewSymbol(buffer); | |
373 | if (string == 0) { | |
374 | FreeTag(subTag); | |
375 | FreeTag(tmpTag); | |
376 | return -1; | |
377 | } | |
378 | ||
379 | tmpTag->type = kTagTypeKey; | |
380 | tmpTag->string = string; | |
381 | tmpTag->tag = subTag; | |
382 | tmpTag->tagNext = 0; | |
383 | ||
384 | *tag = tmpTag; | |
385 | ||
386 | return length + length2; | |
387 | } | |
388 | ||
389 | ||
390 | static long ParseTagString(char *buffer, TagPtr *tag) | |
391 | { | |
392 | long length; | |
393 | char *string; | |
394 | TagPtr tmpTag; | |
395 | ||
396 | length = FixDataMatchingTag(buffer, kXMLTagString); | |
397 | if (length == -1) return -1; | |
398 | ||
399 | tmpTag = NewTag(); | |
400 | if (tmpTag == 0) return -1; | |
401 | ||
402 | string = NewSymbol(buffer); | |
403 | if (string == 0) { | |
404 | FreeTag(tmpTag); | |
405 | return -1; | |
406 | } | |
407 | ||
408 | tmpTag->type = kTagTypeString; | |
409 | tmpTag->string = string; | |
410 | tmpTag->tag = 0; | |
411 | tmpTag->tagNext = 0; | |
412 | ||
413 | *tag = tmpTag; | |
414 | ||
415 | return length; | |
416 | } | |
417 | ||
418 | ||
419 | static long ParseTagInteger(char *buffer, TagPtr *tag) | |
420 | { | |
421 | long length; | |
422 | char *intString; | |
423 | TagPtr tmpTag; | |
424 | ||
425 | length = FixDataMatchingTag(buffer, kXMLTagInteger); | |
426 | if (length == -1) return -1; | |
427 | ||
428 | tmpTag = NewTag(); | |
429 | if (tmpTag == 0) return -1; | |
430 | ||
431 | intString = NewSymbol(buffer); | |
432 | if (intString == 0) { | |
433 | FreeTag(tmpTag); | |
434 | return -1; | |
435 | } | |
436 | ||
437 | tmpTag->type = kTagTypeInteger; | |
438 | tmpTag->string = intString; | |
439 | tmpTag->tag = 0; | |
440 | tmpTag->tagNext = 0; | |
441 | ||
442 | *tag = tmpTag; | |
443 | ||
444 | return length; | |
445 | } | |
446 | ||
447 | ||
448 | static long ParseTagData(char *buffer, TagPtr *tag) | |
449 | { | |
450 | long length; | |
451 | TagPtr tmpTag; | |
452 | ||
453 | length = FixDataMatchingTag(buffer, kXMLTagData); | |
454 | if (length == -1) return -1; | |
455 | ||
456 | tmpTag = NewTag(); | |
457 | if (tmpTag == 0) return -1; | |
458 | ||
459 | tmpTag->type = kTagTypeData; | |
460 | tmpTag->string = 0; | |
461 | tmpTag->tag = 0; | |
462 | tmpTag->tagNext = 0; | |
463 | ||
464 | *tag = tmpTag; | |
465 | ||
466 | return length; | |
467 | } | |
468 | ||
469 | ||
470 | static long ParseTagDate(char *buffer, TagPtr *tag) | |
471 | { | |
472 | long length; | |
473 | TagPtr tmpTag; | |
474 | ||
475 | length = FixDataMatchingTag(buffer, kXMLTagDate); | |
476 | if (length == -1) return -1; | |
477 | ||
478 | tmpTag = NewTag(); | |
479 | if (tmpTag == 0) return -1; | |
480 | ||
481 | tmpTag->type = kTagTypeDate; | |
482 | tmpTag->string = 0; | |
483 | tmpTag->tag = 0; | |
484 | tmpTag->tagNext = 0; | |
485 | ||
486 | *tag = tmpTag; | |
487 | ||
488 | return length; | |
489 | } | |
490 | ||
491 | ||
492 | static long ParseTagBoolean(char *buffer, TagPtr *tag, long type) | |
493 | { | |
494 | TagPtr tmpTag; | |
495 | ||
496 | tmpTag = NewTag(); | |
497 | if (tmpTag == 0) return -1; | |
498 | ||
499 | tmpTag->type = type; | |
500 | tmpTag->string = 0; | |
501 | tmpTag->tag = 0; | |
502 | tmpTag->tagNext = 0; | |
503 | ||
504 | *tag = tmpTag; | |
505 | ||
506 | return 0; | |
507 | } | |
508 | ||
509 | ||
510 | static long GetNextTag(char *buffer, char **tag, long *start, long *empty) | |
511 | { | |
512 | long cnt, cnt2; | |
513 | ||
514 | if (tag == 0) return -1; | |
515 | ||
516 | // Find the start of the tag. | |
517 | cnt = 0; | |
518 | while ((buffer[cnt] != '\0') && (buffer[cnt] != '<')) cnt++; | |
519 | if (buffer[cnt] == '\0') return -1; | |
520 | ||
521 | // Find the end of the tag. | |
522 | cnt2 = cnt + 1; | |
523 | while ((buffer[cnt2] != '\0') && (buffer[cnt2] != '>')) cnt2++; | |
524 | if (buffer[cnt2] == '\0') return -1; | |
525 | if (empty && cnt2 > 1) | |
526 | *empty = buffer[cnt2-1] == '/'; | |
527 | ||
528 | // Fix the tag data. | |
529 | *tag = buffer + cnt + 1; | |
530 | buffer[cnt2] = '\0'; | |
531 | if (start) *start = cnt; | |
532 | ||
533 | return cnt2 + 1; | |
534 | } | |
535 | ||
536 | ||
537 | static long FixDataMatchingTag(char *buffer, char *tag) | |
538 | { | |
539 | long length, start, stop; | |
540 | char *endTag; | |
541 | ||
542 | start = 0; | |
543 | while (1) { | |
544 | length = GetNextTag(buffer + start, &endTag, &stop, NULL); | |
545 | if (length == -1) return -1; | |
546 | ||
547 | if ((*endTag == '/') && !strcmp(endTag + 1, tag)) break; | |
548 | start += length; | |
549 | } | |
550 | ||
551 | buffer[start + stop] = '\0'; | |
552 | ||
553 | return start + length; | |
554 | } | |
555 | ||
556 | ||
557 | #define kTagsPerBlock (0x1000) | |
558 | ||
559 | static TagPtr gTagsFree; | |
560 | ||
561 | static TagPtr NewTag(void) | |
562 | { | |
563 | long cnt; | |
564 | TagPtr tag; | |
565 | ||
566 | if (gTagsFree == 0) { | |
567 | tag = (TagPtr)AllocateBootXMemory(kTagsPerBlock * sizeof(Tag)); | |
568 | if (tag == 0) return 0; | |
569 | ||
570 | // Initalize the new tags. | |
571 | for (cnt = 0; cnt < kTagsPerBlock; cnt++) { | |
572 | tag[cnt].type = kTagTypeNone; | |
573 | tag[cnt].string = 0; | |
574 | tag[cnt].tag = 0; | |
575 | tag[cnt].tagNext = tag + cnt + 1; | |
576 | } | |
577 | tag[kTagsPerBlock - 1].tagNext = 0; | |
578 | ||
579 | gTagsFree = tag; | |
580 | } | |
581 | ||
582 | tag = gTagsFree; | |
583 | gTagsFree = tag->tagNext; | |
584 | ||
585 | return tag; | |
586 | } | |
587 | ||
588 | ||
589 | // currently a no-op | |
590 | void FreeTag(TagPtr tag) | |
591 | { | |
592 | return; // XXXX revisit callers, particularly ParseTagKey (4063982) | |
593 | if (tag == 0) return; | |
594 | ||
595 | if (tag->string) FreeSymbol(tag->string); | |
596 | ||
597 | FreeTag(tag->tag); | |
598 | FreeTag(tag->tagNext); | |
599 | ||
600 | // Clear and free the tag. | |
601 | tag->type = kTagTypeNone; | |
602 | tag->string = 0; | |
603 | tag->tag = 0; | |
604 | tag->tagNext = gTagsFree; | |
605 | gTagsFree = tag; | |
606 | } | |
607 | ||
608 | ||
609 | struct Symbol { | |
610 | long refCount; | |
611 | struct Symbol *next; | |
612 | char string[1]; | |
613 | }; | |
614 | typedef struct Symbol Symbol, *SymbolPtr; | |
615 | ||
616 | static SymbolPtr FindSymbol(char *string, SymbolPtr *prevSymbol); | |
617 | ||
618 | static SymbolPtr gSymbolsHead; | |
619 | ||
620 | ||
621 | static char *NewSymbol(char *string) | |
622 | { | |
623 | SymbolPtr symbol; | |
624 | ||
625 | // Look for string in the list of symbols. | |
626 | symbol = FindSymbol(string, 0); | |
627 | ||
628 | // Add the new symbol. | |
629 | if (symbol == 0) { | |
630 | symbol = AllocateBootXMemory(sizeof(Symbol) + strlen(string)); | |
631 | if (symbol == 0) return 0; | |
632 | ||
633 | // Set the symbol's data. | |
634 | symbol->refCount = 0; | |
635 | strcpy(symbol->string, string); | |
636 | ||
637 | // Add the symbol to the list. | |
638 | symbol->next = gSymbolsHead; | |
639 | gSymbolsHead = symbol; | |
640 | } | |
641 | ||
642 | // Update the refCount and return the string. | |
643 | symbol->refCount++; | |
644 | return symbol->string; | |
645 | } | |
646 | ||
647 | ||
648 | // currently a no-op | |
649 | static void FreeSymbol(char *string) | |
650 | { | |
651 | #if 0 | |
652 | SymbolPtr symbol, prev; | |
653 | ||
654 | // Look for string in the list of symbols. | |
655 | symbol = FindSymbol(string, &prev); | |
656 | if (symbol == 0) return; | |
657 | ||
658 | // Update the refCount. | |
659 | symbol->refCount--; | |
660 | ||
661 | if (symbol->refCount != 0) return; | |
662 | ||
663 | // Remove the symbol from the list. | |
664 | if (prev != 0) prev->next = symbol->next; | |
665 | else gSymbolsHead = symbol->next; | |
666 | ||
667 | // Free the symbol's memory. | |
668 | free(symbol); | |
669 | #endif | |
670 | } | |
671 | ||
672 | ||
673 | static SymbolPtr FindSymbol(char *string, SymbolPtr *prevSymbol) | |
674 | { | |
675 | SymbolPtr symbol, prev; | |
676 | ||
677 | symbol = gSymbolsHead; | |
678 | prev = 0; | |
679 | ||
680 | while (symbol != 0) { | |
681 | if (!strcmp(symbol->string, string)) break; | |
682 | ||
683 | prev = symbol; | |
684 | symbol = symbol->next; | |
685 | } | |
686 | ||
687 | if ((symbol != 0) && (prevSymbol != 0)) *prevSymbol = prev; | |
688 | ||
689 | return symbol; | |
690 | } | |
691 | ||
692 | #if PLIST_DEBUG | |
693 | static void DumpTagDict(TagPtr tag, long depth); | |
694 | static void DumpTagKey(TagPtr tag, long depth); | |
695 | static void DumpTagString(TagPtr tag, long depth); | |
696 | static void DumpTagInteger(TagPtr tag, long depth); | |
697 | static void DumpTagData(TagPtr tag, long depth); | |
698 | static void DumpTagDate(TagPtr tag, long depth); | |
699 | static void DumpTagBoolean(TagPtr tag, long depth); | |
700 | static void DumpTagArray(TagPtr tag, long depth); | |
701 | static void DumpSpaces(long depth); | |
702 | ||
703 | void DumpTag(TagPtr tag, long depth) | |
704 | { | |
705 | if (tag == 0) return; | |
706 | ||
707 | switch (tag->type) { | |
708 | case kTagTypeDict : | |
709 | DumpTagDict(tag, depth); | |
710 | break; | |
711 | ||
712 | case kTagTypeKey : | |
713 | DumpTagKey(tag, depth); | |
714 | break; | |
715 | ||
716 | case kTagTypeString : | |
717 | DumpTagString(tag, depth); | |
718 | break; | |
719 | ||
720 | case kTagTypeInteger : | |
721 | DumpTagInteger(tag, depth); | |
722 | break; | |
723 | ||
724 | case kTagTypeData : | |
725 | DumpTagData(tag, depth); | |
726 | break; | |
727 | ||
728 | case kTagTypeDate : | |
729 | DumpTagDate(tag, depth); | |
730 | break; | |
731 | ||
732 | case kTagTypeFalse : | |
733 | case kTagTypeTrue : | |
734 | DumpTagBoolean(tag, depth); | |
735 | break; | |
736 | ||
737 | case kTagTypeArray : | |
738 | DumpTagArray(tag, depth); | |
739 | break; | |
740 | ||
741 | default : | |
742 | break; | |
743 | } | |
744 | } | |
745 | ||
746 | ||
747 | static void DumpTagDict(TagPtr tag, long depth) | |
748 | { | |
749 | TagPtr tagList; | |
750 | ||
751 | if (tag->tag == 0) { | |
752 | DumpSpaces(depth); | |
753 | printf("<%s/>\n", kXMLTagDict); | |
754 | } else { | |
755 | DumpSpaces(depth); | |
756 | printf("<%s>\n", kXMLTagDict); | |
757 | ||
758 | tagList = tag->tag; | |
759 | while (tagList) { | |
760 | DumpTag(tagList, depth + 1); | |
761 | tagList = tagList->tagNext; | |
762 | } | |
763 | ||
764 | DumpSpaces(depth); | |
765 | printf("</%s>\n", kXMLTagDict); | |
766 | } | |
767 | } | |
768 | ||
769 | ||
770 | static void DumpTagKey(TagPtr tag, long depth) | |
771 | { | |
772 | DumpSpaces(depth); | |
773 | printf("<%s>%s</%s>\n", kXMLTagKey, tag->string, kXMLTagKey); | |
774 | ||
775 | DumpTag(tag->tag, depth); | |
776 | } | |
777 | ||
778 | ||
779 | static void DumpTagString(TagPtr tag, long depth) | |
780 | { | |
781 | DumpSpaces(depth); | |
782 | printf("<%s>%s</%s>\n", kXMLTagString, tag->string, kXMLTagString); | |
783 | } | |
784 | ||
785 | ||
786 | /* integers used to live as char*s but we need 64 bit ints */ | |
787 | static void DumpTagInteger(TagPtr tag, long depth) | |
788 | { | |
789 | DumpSpaces(depth); | |
790 | printf("<%s>%s</%s>\n", kXMLTagInteger, tag->string, kXMLTagInteger); | |
791 | } | |
792 | ||
793 | ||
794 | static void DumpTagData(TagPtr tag, long depth) | |
795 | { | |
796 | DumpSpaces(depth); | |
797 | printf("<%s>%x</%s>\n", kXMLTagData, tag->string, kXMLTagData); | |
798 | } | |
799 | ||
800 | ||
801 | static void DumpTagDate(TagPtr tag, long depth) | |
802 | { | |
803 | DumpSpaces(depth); | |
804 | printf("<%s>%x</%s>\n", kXMLTagDate, tag->string, kXMLTagDate); | |
805 | } | |
806 | ||
807 | ||
808 | static void DumpTagBoolean(TagPtr tag, long depth) | |
809 | { | |
810 | DumpSpaces(depth); | |
811 | printf("<%s>\n", (tag->type == kTagTypeTrue) ? kXMLTagTrue : kXMLTagFalse); | |
812 | } | |
813 | ||
814 | ||
815 | static void DumpTagArray(TagPtr tag, long depth) | |
816 | { | |
817 | TagPtr tagList; | |
818 | ||
819 | if (tag->tag == 0) { | |
820 | DumpSpaces(depth); | |
821 | printf("<%s/>\n", kXMLTagArray); | |
822 | } else { | |
823 | DumpSpaces(depth); | |
824 | printf("<%s>\n", kXMLTagArray); | |
825 | ||
826 | tagList = tag->tag; | |
827 | while (tagList) { | |
828 | DumpTag(tagList, depth + 1); | |
829 | tagList = tagList->tagNext; | |
830 | } | |
831 | ||
832 | DumpSpaces(depth); | |
833 | printf("</%s>\n", kXMLTagArray); | |
834 | } | |
835 | } | |
836 | ||
837 | ||
838 | static void DumpSpaces(long depth) | |
839 | { | |
840 | long cnt; | |
841 | ||
842 | for (cnt = 0; cnt < (depth * 4); cnt++) putchar(' '); | |
843 | } | |
844 | #endif |