1/* 2 * Copyright (c) 2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* CFXMLTree.c 25 Copyright (c) 1999-2013, Apple Inc. All rights reserved. 26 Responsibility: David Smith 27*/ 28 29#include "CFInternal.h" 30#include <CoreFoundation/CFXMLParser.h> 31 32#pragma GCC diagnostic push 33#pragma GCC diagnostic ignored "-Wdeprecated-declarations" 34 35/*************/ 36/* CFXMLTree */ 37/*************/ 38 39/* Creates a childless node from desc */ 40CFXMLTreeRef CFXMLTreeCreateWithNode(CFAllocatorRef allocator, CFXMLNodeRef node) { 41 CFTreeContext treeCtxt; 42 treeCtxt.version = 0; 43 treeCtxt.info = (void *)node; 44 treeCtxt.retain = CFRetain; 45 treeCtxt.release = CFRelease; 46 treeCtxt.copyDescription = CFCopyDescription; 47 return CFTreeCreate(allocator, &treeCtxt); 48} 49 50CFXMLNodeRef CFXMLTreeGetNode(CFXMLTreeRef xmlNode) { 51 CFTreeContext treeContext; 52 treeContext.version = 0; 53 CFTreeGetContext(xmlNode, &treeContext); 54 return (CFXMLNodeRef)treeContext.info; 55} 56 57// We will probably ultimately want to export this under some public API name 58CF_PRIVATE Boolean CFXMLTreeEqual(CFXMLTreeRef xmlTree1, CFXMLTreeRef xmlTree2) { 59 CFXMLNodeRef node1, node2; 60 CFXMLTreeRef child1, child2; 61 if (CFTreeGetChildCount(xmlTree1) != CFTreeGetChildCount(xmlTree2)) return false; 62 node1 = CFXMLTreeGetNode(xmlTree1); 63 node2 = CFXMLTreeGetNode(xmlTree2); 64 if (!CFEqual(node1, node2)) return false; 65 for (child1 = CFTreeGetFirstChild(xmlTree1), child2 = CFTreeGetFirstChild(xmlTree2); child1 && child2; child1 = CFTreeGetNextSibling(child1), child2 = CFTreeGetNextSibling(child2)) { 66 if (!CFXMLTreeEqual(child1, child2)) return false; 67 } 68 return true; 69} 70 71static void _CFAppendXML(CFMutableStringRef str, CFXMLTreeRef tree); 72static void _CFAppendXMLProlog(CFMutableStringRef str, const CFXMLTreeRef node); 73static void _CFAppendXMLEpilog(CFMutableStringRef str, const CFXMLTreeRef node); 74 75CFDataRef CFXMLTreeCreateXMLData(CFAllocatorRef allocator, CFXMLTreeRef xmlTree) { 76 CFMutableStringRef xmlStr; 77 CFDataRef result; 78 CFStringEncoding encoding; 79 80 __CFGenericValidateType(xmlTree, CFTreeGetTypeID()); 81 82 xmlStr = CFStringCreateMutable(allocator, 0); 83 _CFAppendXML(xmlStr, xmlTree); 84 if (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(xmlTree)) == kCFXMLNodeTypeDocument) { 85 const CFXMLDocumentInfo *docData = (CFXMLDocumentInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(xmlTree)); 86 encoding = docData ? docData->encoding : kCFStringEncodingUTF8; 87 } else { 88 encoding = kCFStringEncodingUTF8; 89 } 90 result = CFStringCreateExternalRepresentation(allocator, xmlStr, encoding, 0); 91 CFRelease(xmlStr); 92 return result; 93} 94 95static void _CFAppendXML(CFMutableStringRef str, CFXMLTreeRef tree) { 96 CFXMLTreeRef child; 97 _CFAppendXMLProlog(str, tree); 98 for (child = CFTreeGetFirstChild(tree); child; child = CFTreeGetNextSibling(child)) { 99 _CFAppendXML(str, child); 100 } 101 _CFAppendXMLEpilog(str, tree); 102} 103 104CF_PRIVATE void appendQuotedString(CFMutableStringRef str, CFStringRef strToQuote) { 105 char quoteChar = CFStringFindWithOptions(strToQuote, CFSTR("\""), CFRangeMake(0, CFStringGetLength(strToQuote)), 0, NULL) ? '\'' : '\"'; 106 CFStringAppendFormat(str, NULL, CFSTR("%c%@%c"), quoteChar, strToQuote, quoteChar); 107} 108 109static void appendExternalID(CFMutableStringRef str, CFXMLExternalID *extID) { 110 if (extID->publicID) { 111 CFStringAppendCString(str, " PUBLIC ", kCFStringEncodingASCII); 112 appendQuotedString(str, extID->publicID); 113 if (extID->systemID) { 114 // Technically, for externalIDs, systemID must not be NULL. However, by testing for a NULL systemID, we can use this to emit publicIDs, too. REW, 2/15/2000 115 CFStringAppendCString(str, " ", kCFStringEncodingASCII); 116 appendQuotedString(str, CFURLGetString(extID->systemID)); 117 } 118 } else if (extID->systemID) { 119 CFStringAppendCString(str, " SYSTEM ", kCFStringEncodingASCII); 120 appendQuotedString(str, CFURLGetString(extID->systemID)); 121 } else { 122 // Should never get here 123 } 124} 125 126static void appendElementProlog(CFMutableStringRef str, CFXMLTreeRef tree) { 127 const CFXMLElementInfo *data = (CFXMLElementInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)); 128 CFStringAppendFormat(str, NULL, CFSTR("<%@"), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 129 if (data->attributeOrder) { 130 CFIndex i, c = CFArrayGetCount(data->attributeOrder); 131 for (i = 0; i < c; i ++) { 132 CFStringRef attr = (CFStringRef)CFArrayGetValueAtIndex(data->attributeOrder, i); 133 CFStringRef value = (CFStringRef)CFDictionaryGetValue(data->attributes, attr); 134 CFStringAppendFormat(str, NULL, CFSTR(" %@="), attr); 135 appendQuotedString(str, value); 136 } 137 } 138 if (data->isEmpty) { 139 CFStringAppendCString(str, "/>", kCFStringEncodingASCII); 140 } else { 141 CFStringAppendCString(str, ">", kCFStringEncodingASCII); 142 } 143} 144 145/* Although named "prolog", for leafs of the tree, this is the only XML generation function called. This is why Comments, Processing Instructions, etc. generate their XML during this function. REW, 2/11/2000 */ 146static void _CFAppendXMLProlog(CFMutableStringRef str, const CFXMLTreeRef tree) { 147 switch (CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree))) { 148 case kCFXMLNodeTypeDocument: 149 break; 150 case kCFXMLNodeTypeElement: 151 appendElementProlog(str, tree); 152 break; 153 case kCFXMLNodeTypeAttribute: 154 // Should never be encountered 155 break; 156 case kCFXMLNodeTypeProcessingInstruction: { 157 CFXMLProcessingInstructionInfo *data = (CFXMLProcessingInstructionInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)); 158 if (data->dataString) { 159 CFStringAppendFormat(str, NULL, CFSTR("<?%@ %@?>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)), data->dataString); 160 } else { 161 CFStringAppendFormat(str, NULL, CFSTR("<?%@?>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 162 } 163 break; 164 } 165 case kCFXMLNodeTypeComment: 166 CFStringAppendFormat(str, NULL, CFSTR("<!--%@-->"), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 167 break; 168 case kCFXMLNodeTypeText: 169 CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 170 break; 171 case kCFXMLNodeTypeCDATASection: 172 CFStringAppendFormat(str, NULL, CFSTR("<![CDATA[%@]]>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 173 break; 174 case kCFXMLNodeTypeDocumentFragment: 175 break; 176 case kCFXMLNodeTypeEntity: { 177 CFXMLEntityInfo *data = (CFXMLEntityInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)); 178 CFStringAppendCString(str, "<!ENTITY ", kCFStringEncodingASCII); 179 if (data->entityType == kCFXMLEntityTypeParameter) { 180 CFStringAppend(str, CFSTR("% ")); 181 } 182 CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 183 CFStringAppend(str, CFSTR(" ")); 184 if (data->replacementText) { 185 appendQuotedString(str, data->replacementText); 186 CFStringAppendCString(str, ">", kCFStringEncodingASCII); 187 } else { 188 appendExternalID(str, &(data->entityID)); 189 if (data->notationName) { 190 CFStringAppendFormat(str, NULL, CFSTR(" NDATA %@"), data->notationName); 191 } 192 CFStringAppendCString(str, ">", kCFStringEncodingASCII); 193 } 194 break; 195 } 196 case kCFXMLNodeTypeEntityReference: 197 { 198 CFXMLEntityTypeCode entityType = ((CFXMLEntityReferenceInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->entityType; 199 if (entityType == kCFXMLEntityTypeParameter) { 200 CFStringAppendFormat(str, NULL, CFSTR("%%%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 201 } else { 202 CFStringAppendFormat(str, NULL, CFSTR("&%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 203 } 204 break; 205 } 206 case kCFXMLNodeTypeDocumentType: 207 CFStringAppendCString(str, "<!DOCTYPE ", kCFStringEncodingASCII); 208 CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 209 if (CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree))) { 210 CFXMLExternalID *extID = &((CFXMLDocumentTypeInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->externalID; 211 appendExternalID(str, extID); 212 } 213 CFStringAppendCString(str, " [", kCFStringEncodingASCII); 214 break; 215 case kCFXMLNodeTypeWhitespace: 216 CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 217 break; 218 case kCFXMLNodeTypeNotation: { 219 CFXMLNotationInfo *data = (CFXMLNotationInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)); 220 CFStringAppendFormat(str, NULL, CFSTR("<!NOTATION %@ "), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 221 appendExternalID(str, &(data->externalID)); 222 CFStringAppendCString(str, ">", kCFStringEncodingASCII); 223 break; 224 } 225 case kCFXMLNodeTypeElementTypeDeclaration: 226 CFStringAppendFormat(str, NULL, CFSTR("<!ELEMENT %@ %@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree)), ((CFXMLElementTypeDeclarationInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->contentDescription); 227 break; 228 case kCFXMLNodeTypeAttributeListDeclaration: { 229 CFXMLAttributeListDeclarationInfo *attListData = (CFXMLAttributeListDeclarationInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)); 230 CFIndex idx; 231 CFStringAppendCString(str, "<!ATTLIST ", kCFStringEncodingASCII); 232 CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 233 for (idx = 0; idx < attListData->numberOfAttributes; idx ++) { 234 CFXMLAttributeDeclarationInfo *attr = &(attListData->attributes[idx]); 235 CFStringAppendFormat(str, NULL, CFSTR("\n\t%@ %@ %@"), attr->attributeName, attr->typeString, attr->defaultString); 236 } 237 CFStringAppendCString(str, ">", kCFStringEncodingASCII); 238 break; 239 } 240 default: 241 CFAssert1(false, __kCFLogAssertion, "Encountered unexpected XMLDataTypeID %d", CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree))); 242 } 243} 244 245static void _CFAppendXMLEpilog(CFMutableStringRef str, CFXMLTreeRef tree) { 246 CFXMLNodeTypeCode typeID = CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree)); 247 if (typeID == kCFXMLNodeTypeElement) { 248 if (((CFXMLElementInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->isEmpty) return; 249 CFStringAppendFormat(str, NULL, CFSTR("</%@>"), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); 250 } else if (typeID == kCFXMLNodeTypeDocumentType) { 251 CFIndex len = CFStringGetLength(str); 252 if (CFStringHasSuffix(str, CFSTR(" ["))) { 253 // There were no in-line DTD elements 254 CFStringDelete(str, CFRangeMake(len-2, 2)); 255 } else { 256 CFStringAppendCString(str, "]", kCFStringEncodingASCII); 257 } 258 CFStringAppendCString(str, ">", kCFStringEncodingASCII); 259 } 260} 261 262#pragma GCC diagnostic pop 263