]>
git.saurik.com Git - apple/bootx.git/blob - bootx.tproj/sl.subproj/plist.c
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
23 * plist.c - plist parsing functions
25 * Copyright (c) 2000-2005 Apple Computer, Inc.
28 * code split out from drivers.c by Soren Spies, 2005
33 #define kXMLTagPList "plist"
34 #define kXMLTagDict "dict"
35 #define kXMLTagKey "key"
36 #define kXMLTagString "string"
37 #define kXMLTagInteger "integer"
38 #define kXMLTagData "data"
39 #define kXMLTagDate "date"
40 #define kXMLTagFalse "false/"
41 #define kXMLTagTrue "true/"
42 #define kXMLTagArray "array"
43 // for back-references used by libkern serializer
44 #define kXMLTagReference "reference"
45 #define kXMLTagID "ID="
46 #define kXMLTagIDREF "IDREF="
48 static long ParseNextTag(char *buffer
, TagPtr
*tag
);
49 static long ParseTagList(char *buffer
, TagPtr
*tag
, long type
, long empty
);
50 static long ParseTagKey(char *buffer
, TagPtr
*tag
);
51 static long ParseTagString(char *buffer
, TagPtr
*tag
);
52 static long ParseTagInteger(char *buffer
, TagPtr
*tag
);
53 static long ParseTagData(char *buffer
, TagPtr
*tag
);
54 static long ParseTagDate(char *buffer
, TagPtr
*tag
);
55 static long ParseTagBoolean(char *buffer
, TagPtr
*tag
, long type
);
56 static long GetNextTag(char *buffer
, char **tag
, long *start
, long *empty
);
57 static long FixDataMatchingTag(char *buffer
, char *tag
);
58 static TagPtr
NewTag(void);
59 static char *NewSymbol(char *string
);
60 static void FreeSymbol(char *string
);
63 // for debugging parsing failures
64 static int gTagsParsed
;
65 static char *gLastTag
;
68 TagPtr
GetProperty(TagPtr dict
, char *key
)
72 if (dict
->type
!= kTagTypeDict
) return 0;
78 tagList
= tag
->tagNext
;
80 if ((tag
->type
!= kTagTypeKey
) || (tag
->string
== 0)) continue;
82 if (!strcmp(tag
->string
, key
)) {
90 // intended to look for two versions of the tag; now just for sizeof
91 #define MATCHTAG(parsedTag, keyTag) \
92 (!strncmp(parsedTag, keyTag, sizeof(keyTag)-1))
94 // a tag cache for iokit's super-plists (alas, to merge w/"Symbol" cache?)
95 // we're not going to use the 0th element; it's used by the whole dict anyway
97 static int numids
= 0; // skipping 0th
98 static TagPtr
*idtags
;
100 #define INITIALIDS 10
101 static long InitTagCache()
108 idtags
= (TagPtr
*)malloc(numids
* sizeof(*idtags
));
110 bzero(idtags
, numids
* sizeof(*idtags
));
118 static void FreeTagCache()
125 // get the number from (e.g.): ID="3"
126 static int ExtractID(char *tagName
, int tagLen
)
128 char *idptr
= tagName
+ tagLen
;
131 while(*idptr
!= '>') {
133 if (MATCHTAG(idptr
, kXMLTagID
)) {
134 rval
= strtol(idptr
+ sizeof(kXMLTagID
)-1+1, 0, 0); // -NUL +"
136 } else if (MATCHTAG(idptr
, kXMLTagIDREF
)) {
137 rval
= strtol(idptr
+ sizeof(kXMLTagIDREF
)-1+1, 0, 0); // -NUL +"
140 // there can be multiple modifiers (integers have 'size' first)
141 while(*idptr
!= ' ' && *idptr
!= '>') idptr
++;
147 static TagPtr
TagFromRef(char *tagName
, int tagLen
)
149 int refidx
= ExtractID(tagName
, tagLen
);
152 if (refidx
<= lastid
)
153 rval
= idtags
[refidx
];
158 static long SaveTagRef(TagPtr tag
, int tagid
)
161 // bumped any time we skip an unsupported tag
162 if (tagid
!= ++lastid
) {
163 if (tagid
> lastid
) {
167 printf("invalid plist: tagid (%d) < lastid (%d)??\n", tagid
, lastid
);
172 // upsize idtags if needed
173 if (numids
<= lastid
) {
174 while(numids
<= lastid
)
176 idtags
= (TagPtr
*)realloc(idtags
, numids
* sizeof(*idtags
));
177 if (!idtags
) return -1;
180 // and record for later
181 idtags
[lastid
] = tag
;
186 long ParseXML(char *buffer
, TagPtr
*dict
)
195 if (InitTagCache()) return -1;
198 moduleDict
= (TagPtr
)-1; // have to detect changes to by-ref parameter
199 length
= ParseNextTag(buffer
+ pos
, &moduleDict
);
200 if (length
== -1) break;
203 if (moduleDict
== 0) continue;
205 // did we actually create anything?
206 if (moduleDict
!= (TagPtr
)-1) {
207 if (moduleDict
->type
== kTagTypeDict
) break;
208 if (moduleDict
->type
== kTagTypeArray
) break;
218 printf("ParseXML gagged (-1) after %s (%d tags); buf+pos: %s\n",
219 gLastTag
,gTagsParsed
,buffer
+pos
);
222 // for tidyness even though kext parsing resets all of malloc
225 // return 0 for no error
226 return (length
!= -1) ? 0 : -1;
229 #define PARSESTASHINGTAG(tagBuf, keyTag, parseFunc) do { \
230 TagPtr extantTag = TagFromRef(tagBuf, sizeof(keyTag)-1); \
235 int tagid = ExtractID(tagName, sizeof(keyTag)-1); \
236 length = parseFunc(buffer + pos, tag); \
237 if (tagid && length != -1) \
238 if (-1 == SaveTagRef(*tag, tagid)) \
243 static long ParseNextTag(char *buffer
, TagPtr
*tag
)
245 long length
, pos
, empty
= 0;
249 length
= GetNextTag(buffer
, &tagName
, 0, &empty
);
250 if (length
== -1) return -1;
257 if (MATCHTAG(tagName
, kXMLTagPList
)) {
258 length
= 0; // just a header; nothing to parse
259 // return-via-reference tag should be left alone
260 } else if (MATCHTAG(tagName
, kXMLTagDict
)) {
261 length
= ParseTagList(buffer
+ pos
, tag
, kTagTypeDict
, empty
);
262 } else if (!strcmp(tagName
, kXMLTagKey
)) {
263 length
= ParseTagKey(buffer
+ pos
, tag
);
264 } else if (MATCHTAG(tagName
, kXMLTagReference
) &&
265 (refTag
= TagFromRef(tagName
, sizeof(kXMLTagReference
)-1))) {
268 } else if (MATCHTAG(tagName
, kXMLTagString
)) {
269 PARSESTASHINGTAG(tagName
, kXMLTagString
, ParseTagString
);
270 } else if (MATCHTAG(tagName
, kXMLTagInteger
)) {
271 PARSESTASHINGTAG(tagName
, kXMLTagInteger
, ParseTagInteger
);
272 } else if (!strcmp(tagName
, kXMLTagData
)) {
273 length
= ParseTagData(buffer
+ pos
, tag
);
274 } else if (!strcmp(tagName
, kXMLTagDate
)) {
275 length
= ParseTagDate(buffer
+ pos
, tag
);
276 } else if (!strcmp(tagName
, kXMLTagFalse
)) {
277 length
= ParseTagBoolean(buffer
+ pos
, tag
, kTagTypeFalse
);
278 } else if (!strcmp(tagName
, kXMLTagTrue
)) {
279 length
= ParseTagBoolean(buffer
+ pos
, tag
, kTagTypeTrue
);
280 } else if (MATCHTAG(tagName
, kXMLTagArray
)) {
281 length
= ParseTagList(buffer
+ pos
, tag
, kTagTypeArray
, empty
);
283 // it wasn't parsed so we consumed no additional characters
285 if (tagName
[0] == '/') // was it an end tag (indicated w/*tag = 0)
288 //printf("ignored plist tag: %s (*tag: %x)\n", tagName, *tag);
289 *tag
= (TagPtr
)-1; // we're *not* returning a tag
293 if (length
== -1) return -1;
298 static long ParseTagList(char *buffer
, TagPtr
*tag
, long type
, long empty
)
301 TagPtr tagList
, tmpTag
= (TagPtr
)-1;
309 length
= ParseNextTag(buffer
+ pos
, &tmpTag
);
310 if (length
== -1) break;
313 // detect end of list
314 if (tmpTag
== 0) break;
316 // if we made a new tag, insert into list
317 if (tmpTag
!= (TagPtr
)-1) {
318 tmpTag
->tagNext
= tagList
;
337 tmpTag
->tag
= tagList
;
346 static long ParseTagKey(char *buffer
, TagPtr
*tag
)
348 long length
, length2
;
350 TagPtr tmpTag
, subTag
= (TagPtr
)-1; // eliminate possible stale tag
352 length
= FixDataMatchingTag(buffer
, kXMLTagKey
);
353 if (length
== -1) return -1;
355 length2
= ParseNextTag(buffer
+ length
, &subTag
);
356 if (length2
== -1) return -1;
358 // XXXX revisit 4063982 if FreeTag becomes real
359 if(subTag
== (TagPtr
)-1)
368 string
= NewSymbol(buffer
);
375 tmpTag
->type
= kTagTypeKey
;
376 tmpTag
->string
= string
;
377 tmpTag
->tag
= subTag
;
382 return length
+ length2
;
386 static long ParseTagString(char *buffer
, TagPtr
*tag
)
392 length
= FixDataMatchingTag(buffer
, kXMLTagString
);
393 if (length
== -1) return -1;
396 if (tmpTag
== 0) return -1;
398 string
= NewSymbol(buffer
);
404 tmpTag
->type
= kTagTypeString
;
405 tmpTag
->string
= string
;
415 static long ParseTagInteger(char *buffer
, TagPtr
*tag
)
421 length
= FixDataMatchingTag(buffer
, kXMLTagInteger
);
422 if (length
== -1) return -1;
425 if (tmpTag
== 0) return -1;
427 intString
= NewSymbol(buffer
);
428 if (intString
== 0) {
433 tmpTag
->type
= kTagTypeInteger
;
434 tmpTag
->string
= intString
;
444 static long ParseTagData(char *buffer
, TagPtr
*tag
)
449 length
= FixDataMatchingTag(buffer
, kXMLTagData
);
450 if (length
== -1) return -1;
453 if (tmpTag
== 0) return -1;
455 tmpTag
->type
= kTagTypeData
;
466 static long ParseTagDate(char *buffer
, TagPtr
*tag
)
471 length
= FixDataMatchingTag(buffer
, kXMLTagDate
);
472 if (length
== -1) return -1;
475 if (tmpTag
== 0) return -1;
477 tmpTag
->type
= kTagTypeDate
;
488 static long ParseTagBoolean(char *buffer
, TagPtr
*tag
, long type
)
493 if (tmpTag
== 0) return -1;
506 static long GetNextTag(char *buffer
, char **tag
, long *start
, long *empty
)
510 if (tag
== 0) return -1;
512 // Find the start of the tag.
514 while ((buffer
[cnt
] != '\0') && (buffer
[cnt
] != '<')) cnt
++;
515 if (buffer
[cnt
] == '\0') return -1;
517 // Find the end of the tag.
519 while ((buffer
[cnt2
] != '\0') && (buffer
[cnt2
] != '>')) cnt2
++;
520 if (buffer
[cnt2
] == '\0') return -1;
521 if (empty
&& cnt2
> 1)
522 *empty
= buffer
[cnt2
-1] == '/';
525 *tag
= buffer
+ cnt
+ 1;
527 if (start
) *start
= cnt
;
533 static long FixDataMatchingTag(char *buffer
, char *tag
)
535 long length
, start
, stop
;
540 length
= GetNextTag(buffer
+ start
, &endTag
, &stop
, NULL
);
541 if (length
== -1) return -1;
543 if ((*endTag
== '/') && !strcmp(endTag
+ 1, tag
)) break;
547 buffer
[start
+ stop
] = '\0';
549 return start
+ length
;
553 #define kTagsPerBlock (0x1000)
555 static TagPtr gTagsFree
;
557 static TagPtr
NewTag(void)
562 if (gTagsFree
== 0) {
563 tag
= (TagPtr
)AllocateBootXMemory(kTagsPerBlock
* sizeof(Tag
));
564 if (tag
== 0) return 0;
566 // Initalize the new tags.
567 for (cnt
= 0; cnt
< kTagsPerBlock
; cnt
++) {
568 tag
[cnt
].type
= kTagTypeNone
;
571 tag
[cnt
].tagNext
= tag
+ cnt
+ 1;
573 tag
[kTagsPerBlock
- 1].tagNext
= 0;
579 gTagsFree
= tag
->tagNext
;
586 void FreeTag(TagPtr tag
)
588 return; // XXXX revisit callers, particularly ParseTagKey (4063982)
589 if (tag
== 0) return;
591 if (tag
->string
) FreeSymbol(tag
->string
);
594 FreeTag(tag
->tagNext
);
596 // Clear and free the tag.
597 tag
->type
= kTagTypeNone
;
600 tag
->tagNext
= gTagsFree
;
610 typedef struct Symbol Symbol
, *SymbolPtr
;
612 static SymbolPtr
FindSymbol(char *string
, SymbolPtr
*prevSymbol
);
614 static SymbolPtr gSymbolsHead
;
617 static char *NewSymbol(char *string
)
621 // Look for string in the list of symbols.
622 symbol
= FindSymbol(string
, 0);
624 // Add the new symbol.
626 symbol
= AllocateBootXMemory(sizeof(Symbol
) + strlen(string
));
627 if (symbol
== 0) return 0;
629 // Set the symbol's data.
630 symbol
->refCount
= 0;
631 strcpy(symbol
->string
, string
);
633 // Add the symbol to the list.
634 symbol
->next
= gSymbolsHead
;
635 gSymbolsHead
= symbol
;
638 // Update the refCount and return the string.
640 return symbol
->string
;
645 static void FreeSymbol(char *string
)
648 SymbolPtr symbol
, prev
;
650 // Look for string in the list of symbols.
651 symbol
= FindSymbol(string
, &prev
);
652 if (symbol
== 0) return;
654 // Update the refCount.
657 if (symbol
->refCount
!= 0) return;
659 // Remove the symbol from the list.
660 if (prev
!= 0) prev
->next
= symbol
->next
;
661 else gSymbolsHead
= symbol
->next
;
663 // Free the symbol's memory.
669 static SymbolPtr
FindSymbol(char *string
, SymbolPtr
*prevSymbol
)
671 SymbolPtr symbol
, prev
;
673 symbol
= gSymbolsHead
;
676 while (symbol
!= 0) {
677 if (!strcmp(symbol
->string
, string
)) break;
680 symbol
= symbol
->next
;
683 if ((symbol
!= 0) && (prevSymbol
!= 0)) *prevSymbol
= prev
;
689 static void DumpTagDict(TagPtr tag
, long depth
);
690 static void DumpTagKey(TagPtr tag
, long depth
);
691 static void DumpTagString(TagPtr tag
, long depth
);
692 static void DumpTagInteger(TagPtr tag
, long depth
);
693 static void DumpTagData(TagPtr tag
, long depth
);
694 static void DumpTagDate(TagPtr tag
, long depth
);
695 static void DumpTagBoolean(TagPtr tag
, long depth
);
696 static void DumpTagArray(TagPtr tag
, long depth
);
697 static void DumpSpaces(long depth
);
699 void DumpTag(TagPtr tag
, long depth
)
701 if (tag
== 0) return;
705 DumpTagDict(tag
, depth
);
709 DumpTagKey(tag
, depth
);
712 case kTagTypeString
:
713 DumpTagString(tag
, depth
);
716 case kTagTypeInteger
:
717 DumpTagInteger(tag
, depth
);
721 DumpTagData(tag
, depth
);
725 DumpTagDate(tag
, depth
);
730 DumpTagBoolean(tag
, depth
);
734 DumpTagArray(tag
, depth
);
743 static void DumpTagDict(TagPtr tag
, long depth
)
749 printf("<%s/>\n", kXMLTagDict
);
752 printf("<%s>\n", kXMLTagDict
);
756 DumpTag(tagList
, depth
+ 1);
757 tagList
= tagList
->tagNext
;
761 printf("</%s>\n", kXMLTagDict
);
766 static void DumpTagKey(TagPtr tag
, long depth
)
769 printf("<%s>%s</%s>\n", kXMLTagKey
, tag
->string
, kXMLTagKey
);
771 DumpTag(tag
->tag
, depth
);
775 static void DumpTagString(TagPtr tag
, long depth
)
778 printf("<%s>%s</%s>\n", kXMLTagString
, tag
->string
, kXMLTagString
);
782 /* integers used to live as char*s but we need 64 bit ints */
783 static void DumpTagInteger(TagPtr tag
, long depth
)
786 printf("<%s>%s</%s>\n", kXMLTagInteger
, tag
->string
, kXMLTagInteger
);
790 static void DumpTagData(TagPtr tag
, long depth
)
793 printf("<%s>%x</%s>\n", kXMLTagData
, tag
->string
, kXMLTagData
);
797 static void DumpTagDate(TagPtr tag
, long depth
)
800 printf("<%s>%x</%s>\n", kXMLTagDate
, tag
->string
, kXMLTagDate
);
804 static void DumpTagBoolean(TagPtr tag
, long depth
)
807 printf("<%s>\n", (tag
->type
== kTagTypeTrue
) ? kXMLTagTrue
: kXMLTagFalse
);
811 static void DumpTagArray(TagPtr tag
, long depth
)
817 printf("<%s/>\n", kXMLTagArray
);
820 printf("<%s>\n", kXMLTagArray
);
824 DumpTag(tagList
, depth
+ 1);
825 tagList
= tagList
->tagNext
;
829 printf("</%s>\n", kXMLTagArray
);
834 static void DumpSpaces(long depth
)
838 for (cnt
= 0; cnt
< (depth
* 4); cnt
++) putchar(' ');