CloverBootloader/rEFIt_UEFI/Platform/plist/plist.cpp
2024-01-07 15:54:17 +01:00

815 lines
20 KiB
C++

/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This 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 OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* plist.c - plist parsing functions
*
* Copyright (c) 2000-2005 Apple Computer, Inc.
*
* DRI: Josh de Cesare
* code split out from drivers.c by Soren Spies, 2005
*/
//Slice - rewrite for UEFI with more functions like Copyright (c) 2003 Apple Computer
#include <Platform.h> // Only use angled for Platform, else, xcode project won't compile
#include "../../cpp_foundation/apd.h"
#include "../b64cdecode.h"
#include "plist.h"
#include "../../libeg/FloatLib.h"
#include "xml.h"
#ifndef DEBUG_ALL
#define DEBUG_PLIST 0
#else
#define DEBUG_PLIST DEBUG_ALL
#endif
#if DEBUG_PLIST == 0
#define DBG(...)
#else
#define DBG(...) DebugLog(DEBUG_PLIST, __VA_ARGS__)
#endif
CHAR8* buffer_start = NULL;
// Forward declarations
EFI_STATUS ParseTagDict( CHAR8* buffer, TagStruct* * tag, UINT32 empty, UINT32* lenPtr);
EFI_STATUS ParseTagArray( CHAR8* buffer, TagStruct* * tag, UINT32 empty, UINT32* lenPtr);
EFI_STATUS ParseTagKey( char * buffer, TagStruct* * tag, UINT32* lenPtr);
EFI_STATUS ParseTagString(CHAR8* buffer, TagStruct* * tag, UINT32* lenPtr);
EFI_STATUS ParseTagInteger(CHAR8* buffer, TagStruct* * tag, UINT32* lenPtr);
EFI_STATUS ParseTagFloat(CHAR8* buffer, TagStruct* * tag, UINT32* lenPtr);
EFI_STATUS ParseTagData(CHAR8* buffer, TagStruct* * tag, UINT32* lenPtr);
EFI_STATUS ParseTagDate(CHAR8* buffer, TagStruct* * tag, UINT32* lenPtr);
EFI_STATUS ParseTagBoolean(TagStruct* * tag, XBool value, UINT32* lenPtr);
EFI_STATUS XMLParseNextTag (CHAR8 *buffer, TagStruct**tag, UINT32 *lenPtr);
EFI_STATUS FixDataMatchingTag( CHAR8* buffer, CONST CHAR8* tag,UINT32* lenPtr);
/**************************************** TagStruct ****************************************/
#include "TagDict.h"
#include "TagKey.h"
#include "TagBool.h"
#include "TagData.h"
#include "TagDate.h"
#include "TagArray.h"
#include "TagFloat.h"
#include "TagInt64.h"
#include "TagString8.h"
#ifdef TagStruct_COUNT_CACHEHITMISS
size_t TagStruct::cachemiss = 0;
size_t TagStruct::cachehit = 0;
#endif
void TagStruct::EmptyCache()
{
#ifdef TagStruct_USE_CACHE
TagArray::tagsFree.setEmpty();
TagBool::tagsFree.setEmpty();
TagData::tagsFree.setEmpty();
TagDate::tagsFree.setEmpty();
TagDict::tagsFree.setEmpty();
TagFloat::tagsFree.setEmpty();
TagInt64::tagsFree.setEmpty();
TagKey::tagsFree.setEmpty();
TagString::tagsFree.setEmpty();
#endif
}
XBool TagStruct::debugIsEqual(const TagStruct& other, const XString8& label) const
{
if ( *this != other ) {
MsgLog("Difference at %s\n", label.c_str());
if ( *this != other ) {
}
return false;
}
return true;
}
void TagStruct::printf(unsigned int ident) const
{
XString8 s;
sprintf(ident, &s);
MsgLog("%s", s.c_str());
}
// Convenience method
XBool TagStruct::isTrue() const
{
if ( isBool() ) return getBool()->boolValue();
return false;
}
XBool TagStruct::isFalse() const
{
if ( isBool() ) return !getBool()->boolValue();
return false;
}
XBool TagStruct::isTrueOrYy() const
{
if ( isBool() ) return getBool()->boolValue();
if ( isString() && getString()->stringValue().notEmpty() && (getString()->stringValue()[0] == 'y' || getString()->stringValue()[0] == 'Y') ) return true;
return false;
}
XBool TagStruct::isTrueOrYes() const
{
if ( isBool() ) return getBool()->boolValue();
if ( isString() && getString()->stringValue().isEqual("Yes"_XS8) ) return true;
return false;
}
XBool TagStruct::isFalseOrNn() const
{
if ( isBool() ) return !getBool()->boolValue();
if ( isString() && getString()->stringValue().notEmpty() && (getString()->stringValue()[0] == 'n' || getString()->stringValue()[0] == 'N') ) return true;
return false;
}
/**************************************** XML ****************************************/
// Expects to see one dictionary in the XML file, the final pos will be returned
// If the pos is not equal to the strlen, then there are multiple dicts
// Puts the first dictionary it finds in the
// tag pointer and returns the end of the dic, or returns ?? if not found.
//
EFI_STATUS ParseXML(const UINT8* buffer, TagDict** dict, size_t bufSize)
{
EFI_STATUS Status;
UINT32 length = 0;
UINT32 pos = 0;
TagStruct* tag = NULL;
apd<CHAR8*> configBuffer = NULL;
size_t bufferSize = 0;
UINTN i;
if (bufSize) {
bufferSize = bufSize;
} else {
bufferSize = (UINT32)strlen((CHAR8*)buffer);
}
DBG("buffer size=%ld\n", bufferSize);
if(dict == NULL) {
return EFI_INVALID_PARAMETER;
}
configBuffer = (CHAR8*)malloc(bufferSize+1);
memset(configBuffer, 0, bufferSize+1);
if(configBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
memmove(configBuffer, buffer, bufferSize);
for (i=0; i<bufferSize; i++) {
if (configBuffer[i] == 0) {
configBuffer[i] = 0x20; //replace random zero bytes to spaces
}
}
buffer_start = configBuffer;
while (true)
{
Status = XMLParseNextTag(configBuffer + pos, &tag, &length);
DBG("pos=%u\n", pos);
if (EFI_ERROR(Status)) {
DBG("error parsing next tag\n");
break;
}
pos += length;
if (tag == NULL) {
continue;
}
if (tag->isDict()||tag->isArray()) {
break;
}
tag->ReleaseTag();
tag = NULL;
}
// FreePool(configBuffer);
if (EFI_ERROR(Status)) {
return Status;
}
if (tag->isDict()) *dict = tag->getDict();
else *dict = NULL;
return EFI_SUCCESS;
}
//
// xml
//
#define DOFREE 1
//==========================================================================
// ParseNextTag
EFI_STATUS XMLParseNextTag(CHAR8* buffer, TagStruct** tag, UINT32* lenPtr)
{
EFI_STATUS Status;
UINT32 length = 0;
UINT32 pos = 0;
CHAR8* tagName = NULL;
*lenPtr=0;
Status = GetNextTag((UINT8*)buffer, &tagName, 0, &length);
if (EFI_ERROR(Status)) {
DBG("NextTag error %s\n", efiStrError(Status));
return Status;
}
pos = length;
if (!strncmp(tagName, kXMLTagPList, 6)) {
length=0;
Status=EFI_SUCCESS;
}
/***** dict ****/
else if (!strcmp(tagName, kXMLTagDict))
{
DBG("begin dict len=%d\n", length);
Status = ParseTagDict(buffer + pos, tag, 0, &length);
}
else if (!strcmp(tagName, kXMLTagDict "/"))
{
DBG("end dict len=%d\n", length);
Status = ParseTagDict(buffer + pos, tag, 1, &length);
}
else if (!strncmp(tagName, kXMLTagDict " ", 5))
{
DBG("space dict len=%d\n", length);
Status = ParseTagDict(buffer + pos, tag, 0, &length);
}
/***** key ****/
else if (!strcmp(tagName, kXMLTagKey))
{
DBG("parse key\n");
Status = ParseTagKey(buffer + pos, tag, &length);
}
/***** string ****/
else if (!strcmp(tagName, kXMLTagString))
{
DBG("parse String\n");
Status = ParseTagString(buffer + pos, tag, &length);
}
/***** string ****/
else if (!strncmp(tagName, kXMLTagString " ", 7))
{
DBG("parse String len=%d\n", length);
Status = ParseTagString(buffer + pos, tag, &length);
}
/***** integer ****/
else if (!strcmp(tagName, kXMLTagInteger))
{
Status = ParseTagInteger(buffer + pos, tag, &length);
}
else if (!strncmp(tagName, kXMLTagInteger " ", 8))
{
Status = ParseTagInteger(buffer + pos, tag, &length);
}
/***** float ****/
else if (!strcmp(tagName, kXMLTagFloat))
{
Status = ParseTagFloat(buffer + pos, tag, &length);
}
else if (!strncmp(tagName, kXMLTagFloat " ", 8))
{
Status = ParseTagFloat(buffer + pos, tag, &length);
}
/***** data ****/
else if (!strcmp(tagName, kXMLTagData))
{
Status = ParseTagData(buffer + pos, tag, &length);
}
else if (!strncmp(tagName, kXMLTagData " ", 5))
{
Status = ParseTagData(buffer + pos, tag, &length);
}
/***** date ****/
else if (!strcmp(tagName, kXMLTagDate))
{
Status = ParseTagDate(buffer + pos, tag, &length);
}
/***** false ****/
else if (!strcmp(tagName, kXMLTagFalse))
{
Status = ParseTagBoolean(tag, false, &length);
}
/***** true ****/
else if (!strcmp(tagName, kXMLTagTrue))
{
Status = ParseTagBoolean(tag, true, &length);
}
/***** array ****/
else if (!strcmp(tagName, kXMLTagArray))
{
Status = ParseTagArray(buffer + pos, tag, 0, &length);
}
else if (!strncmp(tagName, kXMLTagArray " ", 6))
{
DBG("begin array len=%d\n", length);
Status = ParseTagArray(buffer + pos, tag, 0, &length);
}
else if (!strcmp(tagName, kXMLTagArray "/"))
{
DBG("end array len=%d\n", length);
Status = ParseTagArray(buffer + pos, tag, 1, &length);
}
/***** unknown ****/
else
{
*tag = NULL;
length = 0;
}
if (EFI_ERROR(Status)) {
return Status;
}
*lenPtr = pos + length;
DBG(" len after success parse next tag %d\n", *lenPtr);
return EFI_SUCCESS;
}
//==========================================================================
// ParseTagList
EFI_STATUS __ParseTagList(XBool isArray, CHAR8* buffer, TagStruct** tag, UINT32 empty, UINT32* lenPtr)
{
EFI_STATUS Status = EFI_SUCCESS;
UINT32 pos;
// TagStruct* tagTail;
UINT32 length = 0;
if (isArray) {
DBG("parsing array len=%d\n", *lenPtr);
} else {
DBG("parsing dict len=%d\n", *lenPtr);
}
// tagTail = NULL;
pos = 0;
TagStruct* dictOrArrayTag;
XObjArray<TagStruct>* tagListPtr;
if (isArray) {
dictOrArrayTag = TagArray::getEmptyTag();
tagListPtr = &dictOrArrayTag->getArray()->arrayContent();
} else {
dictOrArrayTag = TagDict::getEmptyTag();
tagListPtr = &dictOrArrayTag->getDict()->dictContent();
}
XObjArray<TagStruct>& tagList = *tagListPtr;
if (!empty) {
while (true) {
TagStruct* newDictOrArrayTag = NULL;
Status = XMLParseNextTag(buffer + pos, &newDictOrArrayTag, &length);
if (EFI_ERROR(Status)) {
DBG("error XMLParseNextTag in array: %s\n", efiStrError(Status));
break;
}
pos += length;
if (newDictOrArrayTag == NULL) {
break;
}
tagList.AddReference(newDictOrArrayTag, true);
}
if (EFI_ERROR(Status)) {
dictOrArrayTag->ReleaseTag();
return Status;
}
}
*tag = dictOrArrayTag;
*lenPtr=pos;
DBG(" return from ParseTagList with len=%d\n", *lenPtr);
return Status;
}
EFI_STATUS ParseTagDict( CHAR8* buffer, TagStruct** tag, UINT32 empty, UINT32* lenPtr)
{
return __ParseTagList(false, buffer, tag, empty, lenPtr);
}
EFI_STATUS ParseTagArray( CHAR8* buffer, TagStruct** tag, UINT32 empty, UINT32* lenPtr)
{
return __ParseTagList(true, buffer, tag, empty, lenPtr);
}
//==========================================================================
// ParseTagKey
EFI_STATUS ParseTagKey( char * buffer, TagStruct** tag, UINT32* lenPtr)
{
EFI_STATUS Status;
UINT32 length = 0;
UINT32 length2 = 0;
TagKey* tmpTag;
// TagStruct* subTag = NULL;
Status = FixDataMatchingTag(buffer, kXMLTagKey, &length);
DBG("fixing key len=%d status=%s\n", length, efiStrError(Status));
if (EFI_ERROR(Status)){
return Status;
}
// Status = XMLParseNextTag(buffer + length, &subTag, &length2);
// if (EFI_ERROR(Status)) {
// return Status;
// }
tmpTag = TagKey::getEmptyTag();
tmpTag->setKeyValue(buffer, strlen(buffer));
*tag = tmpTag;
*lenPtr = length + length2;
DBG("parse key '%s' success len=%d\n", tmpTag->keyStringValue().c_str(), *lenPtr);
return EFI_SUCCESS;
}
//==========================================================================
// ParseTagString
EFI_STATUS ParseTagString(CHAR8* buffer, TagStruct* * tag,UINT32* lenPtr)
{
EFI_STATUS Status;
UINT32 length = 0;
TagString* tmpTag;
Status = FixDataMatchingTag(buffer, kXMLTagString, &length);
if (EFI_ERROR(Status)) {
return Status;
}
tmpTag = TagString::getEmptyTag();
if (tmpTag == NULL) {
return EFI_OUT_OF_RESOURCES;
}
size_t outlen = XMLDecode(buffer, strlen(buffer), buffer, strlen(buffer));
tmpTag->setStringValue(buffer, outlen);
*tag = tmpTag;
*lenPtr = length;
DBG(" parse string %s\n", tmpTag->getString()->stringValue().c_str());
return EFI_SUCCESS;
}
//==========================================================================
// ParseTagInteger
EFI_STATUS ParseTagInteger(CHAR8* buffer, TagStruct** tag, UINT32* lenPtr)
{
EFI_STATUS Status;
UINT32 length = 0;
INTN integer;
UINT32 size;
XBool negative = false;
CHAR8* val = buffer;
TagInt64* tmpTag;
Status = FixDataMatchingTag(buffer, kXMLTagInteger, &length);
if (EFI_ERROR(Status)) {
return Status;
}
tmpTag = TagInt64::getEmptyTag();
tmpTag->setIntValue(0);
size = length;
integer = 0;
if(buffer[0] == '<')
{
*tag = tmpTag;
length = 0;
return EFI_SUCCESS;
}
if(size > 1 && (val[1] == 'x' || val[1] == 'X')) { // Hex value
val += 2;
while(*val) {
if ((*val >= '0' && *val <= '9')) { // 0 - 9
integer = (integer * 16) + (*val++ - '0');
}
else if ((*val >= 'a' && *val <= 'f')) { // a - f
integer = (integer * 16) + (*val++ - 'a' + 10);
}
else if ((*val >= 'A' && *val <= 'F')) { // A - F
integer = (integer * 16) + (*val++ - 'A' + 10);
}
else {
MsgLog("ParseTagInteger hex error (0x%hhX) in buffer %s\n", *val, buffer);
// getchar();
tmpTag->ReleaseTag();
return EFI_UNSUPPORTED;
}
}
}
else if ( size ) { // Decimal value
if (*val == '-') {
negative = true;
val++;
size--;
}
for (integer = 0; size > 0; size--) {
if(*val) { // UGLY HACK, fix me.
if (*val < '0' || *val > '9') {
MsgLog("ParseTagInteger decimal error (0x%hhX) in buffer %s\n", *val, buffer);
// getchar();
tmpTag->ReleaseTag();
return EFI_UNSUPPORTED;
}
integer = (integer * 10) + (*val++ - '0');
}
}
if (negative) {
integer = -integer;
}
}
tmpTag->setIntValue(integer);
*tag = tmpTag;
*lenPtr = length;
return EFI_SUCCESS;
}
//==========================================================================
// ParseTagFloat
EFI_STATUS ParseTagFloat(CHAR8* buffer, TagStruct* * tag, UINT32* lenPtr)
{
EFI_STATUS Status;
UINT32 length;
TagFloat* tmpTag;
Status = FixDataMatchingTag(buffer, kXMLTagFloat, &length);
if (EFI_ERROR(Status)) {
return Status;
}
tmpTag = TagFloat::getEmptyTag();
//----
float f;
AsciiStrToFloat(buffer, NULL, &f);
//----
tmpTag->setFloatValue(f);
*tag = tmpTag;
*lenPtr = length;
return EFI_SUCCESS;
}
//==========================================================================
// ParseTagData
EFI_STATUS ParseTagData(CHAR8* buffer, TagStruct* * tag, UINT32* lenPtr)
{
EFI_STATUS Status;
UINT32 length = 0;
TagData* tmpTag;
Status = FixDataMatchingTag(buffer, kXMLTagData,&length);
if (EFI_ERROR(Status)) {
return Status;
}
tmpTag = TagData::getEmptyTag();
//Slice - correction as Apple 2003
// tmpTag->setStringValue(buffer);
// dmazar: base64 decode data
UINTN len = 0;
UINT8* data = (UINT8 *)Base64DecodeClover(buffer, &len);
tmpTag->setDataValue(data, len);
*tag = tmpTag;
*lenPtr = length;
return EFI_SUCCESS;
}
//==========================================================================
// ParseTagDate
EFI_STATUS ParseTagDate(CHAR8* buffer, TagStruct* * tag,UINT32* lenPtr)
{
EFI_STATUS Status;
UINT32 length = 0;
TagDate* tmpTag;
Status = FixDataMatchingTag(buffer, kXMLTagDate,&length);
if (EFI_ERROR(Status)) {
return Status;
}
tmpTag = TagDate::getEmptyTag();
tmpTag->setDateValue(buffer, length);
*tag = tmpTag;
*lenPtr = length;
return EFI_SUCCESS;
}
//==========================================================================
// ParseTagBoolean
EFI_STATUS ParseTagBoolean(TagStruct** tag, XBool value, UINT32* lenPtr)
{
TagBool* tmpTag;
tmpTag = TagBool::getEmptyTag();
tmpTag->setBoolValue(value);
*tag = tmpTag;
*lenPtr = 0;
return EFI_SUCCESS;
}
//==========================================================================
// GetNextTag
EFI_STATUS GetNextTag( UINT8* buffer, CHAR8** tag, UINT32* start, UINT32* length)
{
UINT32 cnt, cnt2;
if (tag == NULL) {
return EFI_INVALID_PARAMETER;
}
// Find the start of the tag.
cnt = 0;
while ((buffer[cnt] != '\0') && (buffer[cnt] != '<')) {
cnt++;
}
if (buffer[cnt] == '\0') {
DBG("empty buffer at cnt=%d\n", cnt);
return EFI_UNSUPPORTED;
}
// Find the end of the tag.
cnt2 = cnt + 1;
while ((buffer[cnt2] != '\0') && (buffer[cnt2] != '>')) {
cnt2++;
}
if (buffer[cnt2] == '\0') {
DBG("empty buffer at cnt2=%d\n", cnt2);
return EFI_UNSUPPORTED;
}
// Fix the tag data.
*tag = (CHAR8*)(buffer + cnt + 1);
buffer[cnt2] = '\0';
if (start) {
*start = cnt;
}
*length = cnt2 + 1; //unreal to be -1. This is UINT32
return EFI_SUCCESS;
}
//==========================================================================
// FixDataMatchingTag
// Modifies 'buffer' to add a '\0' at the end of the tag matching 'tag'.
// Returns the length of the data found, counting the end tag,
// or -1 if the end tag was not found.
EFI_STATUS FixDataMatchingTag( CHAR8* buffer, CONST CHAR8* tag, UINT32* lenPtr)
{
EFI_STATUS Status;
UINT32 length;
UINT32 start;
UINT32 stop;
CHAR8* endTag;
start = 0;
while (1) {
Status = GetNextTag(((UINT8 *)buffer) + start, &endTag, &stop, &length);
if (EFI_ERROR(Status)) {
return Status;
}
if ((*endTag == '/') && !strcmp(endTag + 1, tag)) {
break;
}
start += length;
}
DBG("fix buffer at pos=%d\n", start + stop);
buffer[start + stop] = '\0';
*lenPtr = start + length;
return EFI_SUCCESS;
}
//==========================================================================
/*
return true if the property present && value = true
else return false
*/
XBool
IsPropertyNotNullAndTrue(const TagStruct* Prop)
{
return Prop != NULL && Prop->isTrueOrYy();
}
/*
return true if the property present && value = false
else return false
*/
XBool
IsPropertyNotNullAndFalse(const TagStruct* Prop)
{
return Prop != NULL && Prop->isFalseOrNn();
}
/*
Possible values
<integer>1234</integer>
<integer>+1234</integer>
<integer>-1234</integer>
<string>0x12abd</string>
*/
INTN
GetPropertyAsInteger(
const TagStruct* Prop,
INTN Default
)
{
if (Prop == NULL) {
return Default;
}
if (Prop->isInt64()) {
return Prop->getInt64()->intValue();
} else if ((Prop->isString()) && Prop->getString()->stringValue().notEmpty()) {
if ( Prop->getString()->stringValue().length() > 1 && (Prop->getString()->stringValue()[1] == 'x' || Prop->getString()->stringValue()[1] == 'X') ) {
return (INTN)AsciiStrHexToUintn(Prop->getString()->stringValue());
}
if (Prop->getString()->stringValue()[0] == '-') {
return -(INTN)AsciiStrDecimalToUintn (Prop->getString()->stringValue().c_str() + 1);
}
// return (INTN)AsciiStrDecimalToUintn (Prop->getString()->stringValue());
return (INTN)AsciiStrDecimalToUintn((Prop->getString()->stringValue()[0] == '+') ? (Prop->getString()->stringValue().c_str() + 1) : Prop->getString()->stringValue().c_str());
} else if (Prop->isData()) {
UINTN Size = Prop->getData()->dataLenValue();
if (Size > 8) Size = 8;
INTN Data = 0;
memcpy(&Data, Prop->getData()->dataValue(), Size);
return Data;
}
return Default;
}
float GetPropertyFloat (const TagStruct* Prop, float Default)
{
if (Prop == NULL) {
return Default;
}
if (Prop->isFloat()) {
return Prop->getFloat()->floatValue(); //this is union char* or float
} else if ((Prop->isString()) && Prop->getString()->stringValue().notEmpty()) {
float fVar = 0.f;
if(!AsciiStrToFloat(Prop->getString()->stringValue().c_str(), NULL, &fVar)) //if success then return 0
return fVar;
}
return Default;
}