]>
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 PLIST_DEBUG 1 // whether to report detailed parsing errors
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="
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
);
65 // for debugging parsing failures
66 static int gTagsParsed
;
67 static char *gLastTag
;
70 TagPtr
GetProperty(TagPtr dict
, char *key
)
74 if (dict
->type
!= kTagTypeDict
) return 0;
80 tagList
= tag
->tagNext
;
82 if ((tag
->type
!= kTagTypeKey
) || (tag
->string
== 0)) continue;
84 if (!strcmp(tag
->string
, key
)) {
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))
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
99 static int numids
= 0; // skipping 0th
100 static TagPtr
*idtags
;
102 #define INITIALIDS 10
103 static long InitTagCache()
110 idtags
= (TagPtr
*)malloc(numids
* sizeof(*idtags
));
112 bzero(idtags
, numids
* sizeof(*idtags
));
120 static void FreeTagCache()
127 // get the number from (e.g.): ID="3"
128 static int ExtractID(char *tagName
, int tagLen
)
130 char *idptr
= tagName
+ tagLen
;
133 while(*idptr
!= '>') {
135 if (MATCHTAG(idptr
, kXMLTagID
)) {
136 rval
= strtol(idptr
+ sizeof(kXMLTagID
)-1+1, 0, 0); // -NUL +"
138 } else if (MATCHTAG(idptr
, kXMLTagIDREF
)) {
139 rval
= strtol(idptr
+ sizeof(kXMLTagIDREF
)-1+1, 0, 0); // -NUL +"
142 // there can be multiple modifiers (integers have 'size' first)
143 while(*idptr
!= ' ' && *idptr
!= '>') idptr
++;
149 static TagPtr
TagFromRef(char *tagName
, int tagLen
)
151 int refidx
= ExtractID(tagName
, tagLen
);
154 if (refidx
<= lastid
)
155 rval
= idtags
[refidx
];
160 static long SaveTagRef(TagPtr tag
, int tagid
)
163 // bumped any time we skip an unsupported tag
164 if (tagid
!= ++lastid
) {
165 if (tagid
> lastid
) {
169 printf("invalid plist: tagid (%d) < lastid (%d)??\n", tagid
, lastid
);
174 // upsize idtags if needed
175 if (numids
<= lastid
) {
176 while(numids
<= lastid
)
178 idtags
= (TagPtr
*)realloc(idtags
, numids
* sizeof(*idtags
));
179 if (!idtags
) return -1;
182 // and record for later
183 idtags
[lastid
] = tag
;
188 long ParseXML(char *buffer
, TagPtr
*dict
)
197 if (InitTagCache()) return -1;
200 moduleDict
= (TagPtr
)-1; // have to detect changes to by-ref parameter
201 length
= ParseNextTag(buffer
+ pos
, &moduleDict
);
202 if (length
== -1) break;
205 if (moduleDict
== 0) continue;
207 // did we actually create anything?
208 if (moduleDict
!= (TagPtr
)-1) {
209 if (moduleDict
->type
== kTagTypeDict
) break;
210 if (moduleDict
->type
== kTagTypeArray
) break;
221 printf("ParseXML gagged (-1) after '%s' (%d tags); buf+pos: %s\n",
222 gLastTag
,gTagsParsed
,buffer
+pos
);
226 // for tidyness even though kext parsing resets all of malloc
229 // return 0 for no error
230 return (length
!= -1) ? 0 : -1;
233 #define PARSESTASHINGTAG(tagBuf, keyTag, parseFunc) do { \
234 TagPtr extantTag = TagFromRef(tagBuf, sizeof(keyTag)-1); \
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)) \
247 static long ParseNextTag(char *buffer
, TagPtr
*tag
)
249 long length
, pos
, empty
= 0;
253 length
= GetNextTag(buffer
, &tagName
, 0, &empty
);
254 if (length
== -1) return -1;
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))) {
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
);
287 // it wasn't parsed so we consumed no additional characters
289 if (tagName
[0] == '/') // was it an end tag (indicated w/*tag = 0)
292 //printf("ignored plist tag: %s (*tag: %x)\n", tagName, *tag);
293 *tag
= (TagPtr
)-1; // we're *not* returning a tag
297 if (length
== -1) return -1;
302 static long ParseTagList(char *buffer
, TagPtr
*tag
, long type
, long empty
)
305 TagPtr tagList
, tmpTag
= (TagPtr
)-1;
313 length
= ParseNextTag(buffer
+ pos
, &tmpTag
);
314 if (length
== -1) break;
317 // detect end of list
318 if (tmpTag
== 0) break;
320 // if we made a new tag, insert into list
321 if (tmpTag
!= (TagPtr
)-1) {
322 tmpTag
->tagNext
= tagList
;
341 tmpTag
->tag
= tagList
;
350 static long ParseTagKey(char *buffer
, TagPtr
*tag
)
352 long length
, length2
;
354 TagPtr tmpTag
, subTag
= (TagPtr
)-1; // eliminate possible stale tag
356 length
= FixDataMatchingTag(buffer
, kXMLTagKey
);
357 if (length
== -1) return -1;
359 length2
= ParseNextTag(buffer
+ length
, &subTag
);
360 if (length2
== -1) return -1;
362 // XXXX revisit 4063982 if FreeTag becomes real
363 if(subTag
== (TagPtr
)-1)
372 string
= NewSymbol(buffer
);
379 tmpTag
->type
= kTagTypeKey
;
380 tmpTag
->string
= string
;
381 tmpTag
->tag
= subTag
;
386 return length
+ length2
;
390 static long ParseTagString(char *buffer
, TagPtr
*tag
)
396 length
= FixDataMatchingTag(buffer
, kXMLTagString
);
397 if (length
== -1) return -1;
400 if (tmpTag
== 0) return -1;
402 string
= NewSymbol(buffer
);
408 tmpTag
->type
= kTagTypeString
;
409 tmpTag
->string
= string
;
419 static long ParseTagInteger(char *buffer
, TagPtr
*tag
)
425 length
= FixDataMatchingTag(buffer
, kXMLTagInteger
);
426 if (length
== -1) return -1;
429 if (tmpTag
== 0) return -1;
431 intString
= NewSymbol(buffer
);
432 if (intString
== 0) {
437 tmpTag
->type
= kTagTypeInteger
;
438 tmpTag
->string
= intString
;
448 static long ParseTagData(char *buffer
, TagPtr
*tag
)
453 length
= FixDataMatchingTag(buffer
, kXMLTagData
);
454 if (length
== -1) return -1;
457 if (tmpTag
== 0) return -1;
459 tmpTag
->type
= kTagTypeData
;
470 static long ParseTagDate(char *buffer
, TagPtr
*tag
)
475 length
= FixDataMatchingTag(buffer
, kXMLTagDate
);
476 if (length
== -1) return -1;
479 if (tmpTag
== 0) return -1;
481 tmpTag
->type
= kTagTypeDate
;
492 static long ParseTagBoolean(char *buffer
, TagPtr
*tag
, long type
)
497 if (tmpTag
== 0) return -1;
510 static long GetNextTag(char *buffer
, char **tag
, long *start
, long *empty
)
514 if (tag
== 0) return -1;
516 // Find the start of the tag.
518 while ((buffer
[cnt
] != '\0') && (buffer
[cnt
] != '<')) cnt
++;
519 if (buffer
[cnt
] == '\0') return -1;
521 // Find the end of the tag.
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] == '/';
529 *tag
= buffer
+ cnt
+ 1;
531 if (start
) *start
= cnt
;
537 static long FixDataMatchingTag(char *buffer
, char *tag
)
539 long length
, start
, stop
;
544 length
= GetNextTag(buffer
+ start
, &endTag
, &stop
, NULL
);
545 if (length
== -1) return -1;
547 if ((*endTag
== '/') && !strcmp(endTag
+ 1, tag
)) break;
551 buffer
[start
+ stop
] = '\0';
553 return start
+ length
;
557 #define kTagsPerBlock (0x1000)
559 static TagPtr gTagsFree
;
561 static TagPtr
NewTag(void)
566 if (gTagsFree
== 0) {
567 tag
= (TagPtr
)AllocateBootXMemory(kTagsPerBlock
* sizeof(Tag
));
568 if (tag
== 0) return 0;
570 // Initalize the new tags.
571 for (cnt
= 0; cnt
< kTagsPerBlock
; cnt
++) {
572 tag
[cnt
].type
= kTagTypeNone
;
575 tag
[cnt
].tagNext
= tag
+ cnt
+ 1;
577 tag
[kTagsPerBlock
- 1].tagNext
= 0;
583 gTagsFree
= tag
->tagNext
;
590 void FreeTag(TagPtr tag
)
592 return; // XXXX revisit callers, particularly ParseTagKey (4063982)
593 if (tag
== 0) return;
595 if (tag
->string
) FreeSymbol(tag
->string
);
598 FreeTag(tag
->tagNext
);
600 // Clear and free the tag.
601 tag
->type
= kTagTypeNone
;
604 tag
->tagNext
= gTagsFree
;
614 typedef struct Symbol Symbol
, *SymbolPtr
;
616 static SymbolPtr
FindSymbol(char *string
, SymbolPtr
*prevSymbol
);
618 static SymbolPtr gSymbolsHead
;
621 static char *NewSymbol(char *string
)
625 // Look for string in the list of symbols.
626 symbol
= FindSymbol(string
, 0);
628 // Add the new symbol.
630 symbol
= AllocateBootXMemory(sizeof(Symbol
) + strlen(string
));
631 if (symbol
== 0) return 0;
633 // Set the symbol's data.
634 symbol
->refCount
= 0;
635 strcpy(symbol
->string
, string
);
637 // Add the symbol to the list.
638 symbol
->next
= gSymbolsHead
;
639 gSymbolsHead
= symbol
;
642 // Update the refCount and return the string.
644 return symbol
->string
;
649 static void FreeSymbol(char *string
)
652 SymbolPtr symbol
, prev
;
654 // Look for string in the list of symbols.
655 symbol
= FindSymbol(string
, &prev
);
656 if (symbol
== 0) return;
658 // Update the refCount.
661 if (symbol
->refCount
!= 0) return;
663 // Remove the symbol from the list.
664 if (prev
!= 0) prev
->next
= symbol
->next
;
665 else gSymbolsHead
= symbol
->next
;
667 // Free the symbol's memory.
673 static SymbolPtr
FindSymbol(char *string
, SymbolPtr
*prevSymbol
)
675 SymbolPtr symbol
, prev
;
677 symbol
= gSymbolsHead
;
680 while (symbol
!= 0) {
681 if (!strcmp(symbol
->string
, string
)) break;
684 symbol
= symbol
->next
;
687 if ((symbol
!= 0) && (prevSymbol
!= 0)) *prevSymbol
= prev
;
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
);
703 void DumpTag(TagPtr tag
, long depth
)
705 if (tag
== 0) return;
709 DumpTagDict(tag
, depth
);
713 DumpTagKey(tag
, depth
);
716 case kTagTypeString
:
717 DumpTagString(tag
, depth
);
720 case kTagTypeInteger
:
721 DumpTagInteger(tag
, depth
);
725 DumpTagData(tag
, depth
);
729 DumpTagDate(tag
, depth
);
734 DumpTagBoolean(tag
, depth
);
738 DumpTagArray(tag
, depth
);
747 static void DumpTagDict(TagPtr tag
, long depth
)
753 printf("<%s/>\n", kXMLTagDict
);
756 printf("<%s>\n", kXMLTagDict
);
760 DumpTag(tagList
, depth
+ 1);
761 tagList
= tagList
->tagNext
;
765 printf("</%s>\n", kXMLTagDict
);
770 static void DumpTagKey(TagPtr tag
, long depth
)
773 printf("<%s>%s</%s>\n", kXMLTagKey
, tag
->string
, kXMLTagKey
);
775 DumpTag(tag
->tag
, depth
);
779 static void DumpTagString(TagPtr tag
, long depth
)
782 printf("<%s>%s</%s>\n", kXMLTagString
, tag
->string
, kXMLTagString
);
786 /* integers used to live as char*s but we need 64 bit ints */
787 static void DumpTagInteger(TagPtr tag
, long depth
)
790 printf("<%s>%s</%s>\n", kXMLTagInteger
, tag
->string
, kXMLTagInteger
);
794 static void DumpTagData(TagPtr tag
, long depth
)
797 printf("<%s>%x</%s>\n", kXMLTagData
, tag
->string
, kXMLTagData
);
801 static void DumpTagDate(TagPtr tag
, long depth
)
804 printf("<%s>%x</%s>\n", kXMLTagDate
, tag
->string
, kXMLTagDate
);
808 static void DumpTagBoolean(TagPtr tag
, long depth
)
811 printf("<%s>\n", (tag
->type
== kTagTypeTrue
) ? kXMLTagTrue
: kXMLTagFalse
);
815 static void DumpTagArray(TagPtr tag
, long depth
)
821 printf("<%s/>\n", kXMLTagArray
);
824 printf("<%s>\n", kXMLTagArray
);
828 DumpTag(tagList
, depth
+ 1);
829 tagList
= tagList
->tagNext
;
833 printf("</%s>\n", kXMLTagArray
);
838 static void DumpSpaces(long depth
)
842 for (cnt
= 0; cnt
< (depth
* 4); cnt
++) putchar(' ');