/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * drivers.c - Driver Loading Functions. * * Copyright (c) 2000 Apple Computer, Inc. * * DRI: Josh de Cesare */ #include #define DRIVER_DEBUG 0 enum { kTagTypeNone = 0, kTagTypeDict, kTagTypeKey, kTagTypeString, kTagTypeInteger, kTagTypeData, kTagTypeDate, kTagTypeFalse, kTagTypeTrue, kTagTypeArray }; #define kXMLTagPList "plist " #define kXMLTagDict "dict" #define kXMLTagKey "key" #define kXMLTagString "string" #define kXMLTagInteger "integer" #define kXMLTagData "data" #define kXMLTagDate "date" #define kXMLTagFalse "false/" #define kXMLTagTrue "true/" #define kXMLTagArray "array" #define kPropCFBundleIdentifier ("CFBundleIdentifier") #define kPropCFBundleExecutable ("CFBundleExecutable") #define kPropOSBundleRequired ("OSBundleRequired") #define kPropOSBundleLibraries ("OSBundleLibraries") #define kPropIOKitPersonalities ("IOKitPersonalities") #define kPropIONameMatch ("IONameMatch") struct Tag { long type; char *string; struct Tag *tag; struct Tag *tagNext; }; typedef struct Tag Tag, *TagPtr; struct Module { struct Module *nextModule; long willLoad; TagPtr dict; char *plistAddr; long plistLength; char *driverPath; }; typedef struct Module Module, *ModulePtr; struct DriverInfo { char *plistAddr; long plistLength; void *moduleAddr; long moduleLength; }; typedef struct DriverInfo DriverInfo, *DriverInfoPtr; #define kDriverPackageSignature1 'MKXT' #define kDriverPackageSignature2 'MOSX' struct DriversPackage { unsigned long signature1; unsigned long signature2; unsigned long length; unsigned long alder32; unsigned long version; unsigned long numDrivers; unsigned long reserved1; unsigned long reserved2; }; typedef struct DriversPackage DriversPackage; enum { kCFBundleType2, kCFBundleType3 }; static long FileLoadDrivers(char *dirSpec, long plugin); static long NetLoadDrivers(char *dirSpec); static long LoadDriverMKext(char *fileSpec); static long LoadDriverPList(char *dirSpec, char *name, long bundleType); static long LoadMatchedModules(void); static long MatchPersonalities(void); static long MatchLibraries(void); static TagPtr GetProperty(TagPtr dict, char *key); static ModulePtr FindModule(char *name); static long ParseXML(char *buffer, ModulePtr *module, TagPtr *personalities); static long ParseNextTag(char *buffer, TagPtr *tag); static long ParseTagList(char *buffer, TagPtr *tag, long type, long empty); static long ParseTagKey(char *buffer, TagPtr *tag); static long ParseTagString(char *buffer, TagPtr *tag); static long ParseTagInteger(char *buffer, TagPtr *tag); static long ParseTagData(char *buffer, TagPtr *tag); static long ParseTagDate(char *buffer, TagPtr *tag); static long ParseTagBoolean(char *buffer, TagPtr *tag, long type); static long GetNextTag(char *buffer, char **tag, long *start); static long FixDataMatchingTag(char *buffer, char *tag); static TagPtr NewTag(void); static void FreeTag(TagPtr tag); static char *NewSymbol(char *string); static void FreeSymbol(char *string); #if DRIVER_DEBUG static void DumpTag(TagPtr tag, long depth); #endif static ModulePtr gModuleHead, gModuleTail; static TagPtr gPersonalityHead, gPersonalityTail; static char gDriverSpec[4096]; static char gFileSpec[4096]; static char gTempSpec[4096]; static char gFileName[4096]; // Public Functions long LoadDrivers(char *dirSpec) { if (gBootFileType == kNetworkDeviceType) { NetLoadDrivers(dirSpec); } else if (gBootFileType == kBlockDeviceType) { FileLoadDrivers(dirSpec, 0); } else { return 0; } MatchPersonalities(); MatchLibraries(); LoadMatchedModules(); return 0; } // Private Functions static long FileLoadDrivers(char *dirSpec, long plugin) { long ret, length, index, flags, time, time2, bundleType; char *name; if (!plugin) { ret = GetFileInfo(dirSpec, "Extensions.mkext", &flags, &time); if ((ret == 0) && ((flags & kFileTypeMask) == kFileTypeFlat)) { ret = GetFileInfo(dirSpec, "Extensions", &flags, &time2); if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeDirectory) || (((gBootMode & kBootModeSafe) == 0) && (time > time2))) { sprintf(gDriverSpec, "%sExtensions.mkext", dirSpec); printf("LoadDrivers: Loading from [%s]\n", gDriverSpec); if (LoadDriverMKext(gDriverSpec) == 0) return 0; } } strcat(dirSpec, "Extensions"); } printf("LoadDrivers: Loading from [%s]\n", dirSpec); index = 0; while (1) { ret = GetDirEntry(dirSpec, &index, &name, &flags, &time); if (ret == -1) break; // Make sure this is a directory. if ((flags & kFileTypeMask ) != kFileTypeDirectory) continue; // Make sure this is a kext. length = strlen(name); if (strcmp(name + length - 5, ".kext")) continue; // Save the file name. strcpy(gFileName, name); // Determine the bundle type. sprintf(gTempSpec, "%s\\%s", dirSpec, gFileName); ret = GetFileInfo(gTempSpec, "Contents", &flags, &time); if (ret == 0) bundleType = kCFBundleType2; else bundleType = kCFBundleType3; if (!plugin) { sprintf(gDriverSpec, "%s\\%s\\%sPlugIns", dirSpec, gFileName, (bundleType == kCFBundleType2) ? "Contents\\" : ""); } ret = LoadDriverPList(dirSpec, gFileName, bundleType); if (ret != 0) { printf("LoadDrivers: failed\n"); } if (!plugin) { ret = FileLoadDrivers(gDriverSpec, 1); } } return 0; } static long NetLoadDrivers(char *dirSpec) { long tries, cnt; // Get the name of the kernel cnt = strlen(gBootFile); while (cnt--) { if ((gBootFile[cnt] == '\\') || (gBootFile[cnt] == ',')) { cnt++; break; } } sprintf(gDriverSpec, "%s%s.mkext", dirSpec, gBootFile + cnt); printf("NetLoadDrivers: Loading from [%s]\n", gDriverSpec); tries = 10; while (tries--) { if (LoadDriverMKext(gDriverSpec) == 0) break; } if (tries == -1) return -1; return 0; } static long LoadDriverMKext(char *fileSpec) { unsigned long driversAddr, driversLength, length; char segName[32]; DriversPackage *package = (DriversPackage *)kLoadAddr; // Load the MKext. length = LoadFile(fileSpec); if (length == -1) return -1; ThinFatBinary((void **)&package, &length); // Verify the MKext. if ((package->signature1 != kDriverPackageSignature1) || (package->signature2 != kDriverPackageSignature2)) return -1; if (package->length > kLoadSize) return -1; if (package->alder32 != Alder32((char *)&package->version, package->length - 0x10)) return -1; // Make space for the MKext. driversLength = package->length; driversAddr = AllocateKernelMemory(driversLength); // Copy the MKext. memcpy((void *)driversAddr, (void *)package, driversLength); // Add the MKext to the memory map. sprintf(segName, "DriversPackage-%x", driversAddr); AllocateMemoryRange(segName, driversAddr, driversLength); return 0; } static long LoadDriverPList(char *dirSpec, char *name, long bundleType) { long length, ret, driverPathLength; char *buffer; ModulePtr module; TagPtr personalities; char *tmpDriverPath; // Reset the malloc zone. malloc_init((char *)kMallocAddr, kMallocSize); // Save the driver path. sprintf(gFileSpec, "%s\\%s\\%s", dirSpec, name, (bundleType == kCFBundleType2) ? "Contents\\MacOS\\" : ""); driverPathLength = strlen(gFileSpec); tmpDriverPath = malloc(driverPathLength + 1); if (tmpDriverPath == 0) return -1; strcpy(tmpDriverPath, gFileSpec); // Construct the file spec. sprintf(gFileSpec, "%s\\%s\\%sInfo.plist", dirSpec, name, (bundleType == kCFBundleType2) ? "Contents\\" : ""); length = LoadFile(gFileSpec); if (length == -1) { free(tmpDriverPath); return -1; } buffer = malloc(length + 1); if (buffer == 0) { free(tmpDriverPath); return -1; } strncpy(buffer, (char *)kLoadAddr, length); ret = ParseXML(buffer, &module, &personalities); free(buffer); if (ret != 0) { free(tmpDriverPath); return -1; } // Allocate memory for the driver path and the plist. module->driverPath = AllocateBootXMemory(driverPathLength + 1); module->plistAddr = AllocateBootXMemory(length + 1); if ((module->driverPath == 0) | (module->plistAddr == 0)) { free(tmpDriverPath); return -1; } // Save the driver path in the module. strcpy(module->driverPath, tmpDriverPath); free(tmpDriverPath); // Add the origin plist to the module. strncpy(module->plistAddr, (char *)kLoadAddr, length); module->plistLength = length + 1; // Add the module to the end of the module list. if (gModuleHead == 0) gModuleHead = module; else gModuleTail->nextModule = module; gModuleTail = module; // Add the persionalities to the personality list. if (personalities) personalities = personalities->tag; while (personalities != 0) { if (gPersonalityHead == 0) gPersonalityHead = personalities->tag; else gPersonalityTail->tagNext = personalities->tag; gPersonalityTail = personalities->tag; personalities = personalities->tagNext; } return 0; } static long LoadMatchedModules(void) { TagPtr prop; ModulePtr module; char *fileName, segName[32]; DriverInfoPtr driver; unsigned long length, driverAddr, driverLength; void *driverModuleAddr; module = gModuleHead; while (module != 0) { if (module->willLoad) { prop = GetProperty(module->dict, kPropCFBundleExecutable); if (prop != 0) { fileName = prop->string; sprintf(gFileSpec, "%s%s", module->driverPath, fileName); length = LoadFile(gFileSpec); } else length = 0; if (length != -1) { if (length != 0) { driverModuleAddr = (void *)kLoadAddr; ThinFatBinary(&driverModuleAddr, &length); } // Make make in the image area. driverLength = sizeof(DriverInfo) + module->plistLength + length; driverAddr = AllocateKernelMemory(driverLength); // Set up the DriverInfo. driver = (DriverInfoPtr)driverAddr; driver->plistAddr = (char *)(driverAddr + sizeof(DriverInfo)); driver->plistLength = module->plistLength; if (length != 0) { driver->moduleAddr = (void *)(driverAddr + sizeof(DriverInfo) + module->plistLength); driver->moduleLength = length; } else { driver->moduleAddr = 0; driver->moduleLength = 0; } // Save the plist and module. strcpy(driver->plistAddr, module->plistAddr); if (length != 0) { memcpy(driver->moduleAddr, driverModuleAddr, driver->moduleLength); } // Add an entry to the memory map. sprintf(segName, "Driver-%x", driver); AllocateMemoryRange(segName, driverAddr, driverLength); } } module = module->nextModule; } return 0; } static long MatchPersonalities(void) { TagPtr persionality; TagPtr prop; ModulePtr module; CICell ph; // Try to match each of the personalities. for(persionality = gPersonalityHead; persionality != 0; persionality = persionality->tagNext) { // Get the module name. Make sure it exists and has not // already been marked for loading. prop = GetProperty(persionality, kPropCFBundleIdentifier); if (prop == 0) continue; module = FindModule(prop->string); if (module == 0) continue; if (module->willLoad) continue; // Look for the exact match property. // Try to match with it. // Look for the old match property. // Try to match with it. ph = 0; prop = GetProperty(persionality, kPropIONameMatch); if ((prop != 0) && (prop->tag != 0)) prop = prop->tag; while (prop != 0) { ph = SearchForNodeMatching(0, 1, prop->string); if (ph != 0) break; prop = prop->tagNext; } // If a node was found mark the module to be loaded. if (ph != 0) { module->willLoad = 1; } } return 0; } static long MatchLibraries(void) { TagPtr prop, prop2; ModulePtr module, module2; long done; do { done = 1; module = gModuleHead; while (module != 0) { if (module->willLoad == 1) { prop = GetProperty(module->dict, kPropOSBundleLibraries); if (prop != 0) { prop = prop->tag; while (prop != 0) { module2 = gModuleHead; while (module2 != 0) { prop2 = GetProperty(module2->dict, kPropCFBundleIdentifier); if ((prop2 != 0) && (!strcmp(prop->string, prop2->string))) { if (module2->willLoad == 0) module2->willLoad = 1; break; } module2 = module2->nextModule; } prop = prop->tagNext; } } module->willLoad = 2; done = 0; } module = module->nextModule; } } while (!done); return 0; } static TagPtr GetProperty(TagPtr dict, char *key) { TagPtr tagList, tag; if (dict->type != kTagTypeDict) return 0; tag = 0; tagList = dict->tag; while (tagList) { tag = tagList; tagList = tag->tagNext; if ((tag->type != kTagTypeKey) || (tag->string == 0)) continue; if (!strcmp(tag->string, key)) return tag->tag; } return 0; } static ModulePtr FindModule(char *name) { ModulePtr module; TagPtr prop; module = gModuleHead; while (module != 0) { prop = GetProperty(module->dict, kPropCFBundleIdentifier); if ((prop != 0) && !strcmp(name, prop->string)) break; module = module->nextModule; } return module; } static long ParseXML(char *buffer, ModulePtr *module, TagPtr *personalities) { long length, pos; TagPtr moduleDict, required; ModulePtr tmpModule; pos = 0; while (1) { length = ParseNextTag(buffer + pos, &moduleDict); if (length == -1) break; pos += length; if (moduleDict == 0) continue; if (moduleDict->type == kTagTypeDict) break; FreeTag(moduleDict); } if (length == -1) return -1; required = GetProperty(moduleDict, kPropOSBundleRequired); if ((required == 0) || (required->type != kTagTypeString) || !strcmp(required->string, "Safe Boot")) { FreeTag(moduleDict); return -1; } tmpModule = AllocateBootXMemory(sizeof(Module)); if (tmpModule == 0) { FreeTag(moduleDict); return -1; } tmpModule->dict = moduleDict; // For now, load any module that has OSBundleRequired != "Safe Boot". tmpModule->willLoad = 1; *module = tmpModule; // Get the personalities. *personalities = GetProperty(moduleDict, kPropIOKitPersonalities); return 0; } static long ParseNextTag(char *buffer, TagPtr *tag) { long length, pos; char *tagName; length = GetNextTag(buffer, &tagName, 0); if (length == -1) return -1; pos = length; if (!strncmp(tagName, kXMLTagPList, 6)) { length = 0; } else if (!strcmp(tagName, kXMLTagDict)) { length = ParseTagList(buffer + pos, tag, kTagTypeDict, 0); } else if (!strcmp(tagName, kXMLTagDict "/")) { length = ParseTagList(buffer + pos, tag, kTagTypeDict, 1); } else if (!strcmp(tagName, kXMLTagKey)) { length = ParseTagKey(buffer + pos, tag); } else if (!strcmp(tagName, kXMLTagString)) { length = ParseTagString(buffer + pos, tag); } else if (!strcmp(tagName, kXMLTagInteger)) { length = ParseTagInteger(buffer + pos, tag); } else if (!strcmp(tagName, kXMLTagData)) { length = ParseTagData(buffer + pos, tag); } else if (!strcmp(tagName, kXMLTagDate)) { length = ParseTagDate(buffer + pos, tag); } else if (!strcmp(tagName, kXMLTagFalse)) { length = ParseTagBoolean(buffer + pos, tag, kTagTypeFalse); } else if (!strcmp(tagName, kXMLTagTrue)) { length = ParseTagBoolean(buffer + pos, tag, kTagTypeTrue); } else if (!strcmp(tagName, kXMLTagArray)) { length = ParseTagList(buffer + pos, tag, kTagTypeArray, 0); } else if (!strcmp(tagName, kXMLTagArray "/")) { length = ParseTagList(buffer + pos, tag, kTagTypeArray, 1); } else { *tag = 0; length = 0; } if (length == -1) return -1; return pos + length; } static long ParseTagList(char *buffer, TagPtr *tag, long type, long empty) { long length, pos; TagPtr tagList, tmpTag; tagList = 0; pos = 0; if (!empty) { while (1) { length = ParseNextTag(buffer + pos, &tmpTag); if (length == -1) break; pos += length; if (tmpTag == 0) break; tmpTag->tagNext = tagList; tagList = tmpTag; } if (length == -1) { FreeTag(tagList); return -1; } } tmpTag = NewTag(); if (tmpTag == 0) { FreeTag(tagList); return -1; } tmpTag->type = type; tmpTag->string = 0; tmpTag->tag = tagList; tmpTag->tagNext = 0; *tag = tmpTag; return pos; } static long ParseTagKey(char *buffer, TagPtr *tag) { long length, length2; char *string; TagPtr tmpTag, subTag; length = FixDataMatchingTag(buffer, kXMLTagKey); if (length == -1) return -1; length2 = ParseNextTag(buffer + length, &subTag); if (length2 == -1) return -1; tmpTag = NewTag(); if (tmpTag == 0) { FreeTag(subTag); return -1; } string = NewSymbol(buffer); if (string == 0) { FreeTag(subTag); FreeTag(tmpTag); return -1; } tmpTag->type = kTagTypeKey; tmpTag->string = string; tmpTag->tag = subTag; tmpTag->tagNext = 0; *tag = tmpTag; return length + length2; } static long ParseTagString(char *buffer, TagPtr *tag) { long length; char *string; TagPtr tmpTag; length = FixDataMatchingTag(buffer, kXMLTagString); if (length == -1) return -1; tmpTag = NewTag(); if (tmpTag == 0) return -1; string = NewSymbol(buffer); if (string == 0) { FreeTag(tmpTag); return -1; } tmpTag->type = kTagTypeString; tmpTag->string = string; tmpTag->tag = 0; tmpTag->tagNext = 0; *tag = tmpTag; return length; } static long ParseTagInteger(char *buffer, TagPtr *tag) { long length, integer; TagPtr tmpTag; length = FixDataMatchingTag(buffer, kXMLTagInteger); if (length == -1) return -1; tmpTag = NewTag(); if (tmpTag == 0) return -1; integer = 0; tmpTag->type = kTagTypeInteger; tmpTag->string = (char *)integer; tmpTag->tag = 0; tmpTag->tagNext = 0; *tag = tmpTag; return length; } static long ParseTagData(char *buffer, TagPtr *tag) { long length; TagPtr tmpTag; length = FixDataMatchingTag(buffer, kXMLTagData); if (length == -1) return -1; tmpTag = NewTag(); if (tmpTag == 0) return -1; tmpTag->type = kTagTypeData; tmpTag->string = 0; tmpTag->tag = 0; tmpTag->tagNext = 0; *tag = tmpTag; return length; } static long ParseTagDate(char *buffer, TagPtr *tag) { long length; TagPtr tmpTag; length = FixDataMatchingTag(buffer, kXMLTagDate); if (length == -1) return -1; tmpTag = NewTag(); if (tmpTag == 0) return -1; tmpTag->type = kTagTypeDate; tmpTag->string = 0; tmpTag->tag = 0; tmpTag->tagNext = 0; *tag = tmpTag; return length; } static long ParseTagBoolean(char *buffer, TagPtr *tag, long type) { TagPtr tmpTag; tmpTag = NewTag(); if (tmpTag == 0) return -1; tmpTag->type = type; tmpTag->string = 0; tmpTag->tag = 0; tmpTag->tagNext = 0; *tag = tmpTag; return 0; } static long GetNextTag(char *buffer, char **tag, long *start) { long cnt, cnt2; if (tag == 0) return -1; // Find the start of the tag. cnt = 0; while ((buffer[cnt] != '\0') && (buffer[cnt] != '<')) cnt++; if (buffer[cnt] == '\0') return -1; // Find the end of the tag. cnt2 = cnt + 1; while ((buffer[cnt2] != '\0') && (buffer[cnt2] != '>')) cnt2++; if (buffer[cnt2] == '\0') return -1; // Fix the tag data. *tag = buffer + cnt + 1; buffer[cnt2] = '\0'; if (start) *start = cnt; return cnt2 + 1; } static long FixDataMatchingTag(char *buffer, char *tag) { long length, start, stop; char *endTag; start = 0; while (1) { length = GetNextTag(buffer + start, &endTag, &stop); if (length == -1) return -1; if ((*endTag == '/') && !strcmp(endTag + 1, tag)) break; start += length; } buffer[start + stop] = '\0'; return start + length; } #define kTagsPerBlock (0x1000) static TagPtr gTagsFree; static TagPtr NewTag(void) { long cnt; TagPtr tag; if (gTagsFree == 0) { tag = (TagPtr)AllocateBootXMemory(kTagsPerBlock * sizeof(Tag)); if (tag == 0) return 0; // Initalize the new tags. for (cnt = 0; cnt < kTagsPerBlock; cnt++) { tag[cnt].type = kTagTypeNone; tag[cnt].string = 0; tag[cnt].tag = 0; tag[cnt].tagNext = tag + cnt + 1; } tag[kTagsPerBlock - 1].tagNext = 0; gTagsFree = tag; } tag = gTagsFree; gTagsFree = tag->tagNext; return tag; } static void FreeTag(TagPtr tag) { return; if (tag == 0) return; if (tag->string) FreeSymbol(tag->string); FreeTag(tag->tag); FreeTag(tag->tagNext); // Clear and free the tag. tag->type = kTagTypeNone; tag->string = 0; tag->tag = 0; tag->tagNext = gTagsFree; gTagsFree = tag; } struct Symbol { long refCount; struct Symbol *next; char string[1]; }; typedef struct Symbol Symbol, *SymbolPtr; static SymbolPtr FindSymbol(char *string, SymbolPtr *prevSymbol); static SymbolPtr gSymbolsHead; static char *NewSymbol(char *string) { SymbolPtr symbol; // Look for string in the list of symbols. symbol = FindSymbol(string, 0); // Add the new symbol. if (symbol == 0) { symbol = AllocateBootXMemory(sizeof(Symbol) + strlen(string)); if (symbol == 0) return 0; // Set the symbol's data. symbol->refCount = 0; strcpy(symbol->string, string); // Add the symbol to the list. symbol->next = gSymbolsHead; gSymbolsHead = symbol; } // Update the refCount and return the string. symbol->refCount++; return symbol->string; } static void FreeSymbol(char *string) { #if 0 SymbolPtr symbol, prev; // Look for string in the list of symbols. symbol = FindSymbol(string, &prev); if (symbol == 0) return; // Update the refCount. symbol->refCount--; if (symbol->refCount != 0) return; // Remove the symbol from the list. if (prev != 0) prev->next = symbol->next; else gSymbolsHead = symbol->next; // Free the symbol's memory. free(symbol); #endif } static SymbolPtr FindSymbol(char *string, SymbolPtr *prevSymbol) { SymbolPtr symbol, prev; symbol = gSymbolsHead; prev = 0; while (symbol != 0) { if (!strcmp(symbol->string, string)) break; prev = symbol; symbol = symbol->next; } if ((symbol != 0) && (prevSymbol != 0)) *prevSymbol = prev; return symbol; } #if DRIVER_DEBUG static void DumpTagDict(TagPtr tag, long depth); static void DumpTagKey(TagPtr tag, long depth); static void DumpTagString(TagPtr tag, long depth); static void DumpTagInteger(TagPtr tag, long depth); static void DumpTagData(TagPtr tag, long depth); static void DumpTagDate(TagPtr tag, long depth); static void DumpTagBoolean(TagPtr tag, long depth); static void DumpTagArray(TagPtr tag, long depth); static void DumpSpaces(long depth); static void DumpTag(TagPtr tag, long depth) { if (tag == 0) return; switch (tag->type) { case kTagTypeDict : DumpTagDict(tag, depth); break; case kTagTypeKey : DumpTagKey(tag, depth); break; case kTagTypeString : DumpTagString(tag, depth); break; case kTagTypeInteger : DumpTagInteger(tag, depth); break; case kTagTypeData : DumpTagData(tag, depth); break; case kTagTypeDate : DumpTagDate(tag, depth); break; case kTagTypeFalse : case kTagTypeTrue : DumpTagBoolean(tag, depth); break; case kTagTypeArray : DumpTagArray(tag, depth); break; default : break; } } static void DumpTagDict(TagPtr tag, long depth) { TagPtr tagList; if (tag->tag == 0) { DumpSpaces(depth); printf("<%s/>\n", kXMLTagDict); } else { DumpSpaces(depth); printf("<%s>\n", kXMLTagDict); tagList = tag->tag; while (tagList) { DumpTag(tagList, depth + 1); tagList = tagList->tagNext; } DumpSpaces(depth); printf("\n", kXMLTagDict); } } static void DumpTagKey(TagPtr tag, long depth) { DumpSpaces(depth); printf("<%s>%s\n", kXMLTagKey, tag->string, kXMLTagKey); DumpTag(tag->tag, depth); } static void DumpTagString(TagPtr tag, long depth) { DumpSpaces(depth); printf("<%s>%s\n", kXMLTagString, tag->string, kXMLTagString); } static void DumpTagInteger(TagPtr tag, long depth) { DumpSpaces(depth); printf("<%s>%x\n", kXMLTagInteger, tag->string, kXMLTagInteger); } static void DumpTagData(TagPtr tag, long depth) { DumpSpaces(depth); printf("<%s>%x\n", kXMLTagData, tag->string, kXMLTagData); } static void DumpTagDate(TagPtr tag, long depth) { DumpSpaces(depth); printf("<%s>%x\n", kXMLTagDate, tag->string, kXMLTagDate); } static void DumpTagBoolean(TagPtr tag, long depth) { DumpSpaces(depth); printf("<%s>\n", (tag->type == kTagTypeTrue) ? kXMLTagTrue : kXMLTagFalse); } static void DumpTagArray(TagPtr tag, long depth) { TagPtr tagList; if (tag->tag == 0) { DumpSpaces(depth); printf("<%s/>\n", kXMLTagArray); } else { DumpSpaces(depth); printf("<%s>\n", kXMLTagArray); tagList = tag->tag; while (tagList) { DumpTag(tagList, depth + 1); tagList = tagList->tagNext; } DumpSpaces(depth); printf("\n", kXMLTagArray); } } static void DumpSpaces(long depth) { long cnt; for (cnt = 0; cnt < (depth * 4); cnt++) putchar(' '); } #endif