2812 lines
66 KiB
C++
2812 lines
66 KiB
C++
/**
|
|
* @file kXml.cpp
|
|
*
|
|
* @internal
|
|
* Copyright (C) 2004-2021 by LMI Technologies Inc.
|
|
* Licensed under the MIT License.
|
|
* Redistributed files must retain the above copyright notice.
|
|
*/
|
|
#include <kApi/Data/kXml.h>
|
|
#include <kApi/Data/kArrayList.h>
|
|
#include <kApi/Data/kMath.h>
|
|
#include <kApi/Data/kString.h>
|
|
#include <kApi/Io/kFile.h>
|
|
#include <kApi/Io/kMemory.h>
|
|
#include <kApi/Io/kSerializer.h>
|
|
|
|
struct EscSeq
|
|
{
|
|
char character;
|
|
char szSeq[25];
|
|
};
|
|
|
|
static struct EscSeq escSeqs[] = {
|
|
{ '&', "amp" },
|
|
{ '<', "lt" },
|
|
{ '>', "gt" },
|
|
{ '\"', "quot" },
|
|
{ '\'', "apos" },
|
|
{ 0xA, "#x0A" },
|
|
{ 0xD, "#x0D" }
|
|
};
|
|
|
|
kBeginClassEx(k, kXml)
|
|
|
|
kAddPrivateVersionEx(kXml, "kdat6", "6.0.0.0", "kXml-0", WriteDat6V0, ReadDat6V0)
|
|
|
|
kAddFrameworkConstructor(kXml, Construct)
|
|
|
|
kAddPrivateVMethod(kXml, kObject, VRelease)
|
|
kAddPrivateVMethod(kXml, kObject, VClone)
|
|
kEndClassEx()
|
|
|
|
kBeginValueEx(k, xkXmlItemBlock)
|
|
kAddField(xkXmlItemBlock, kPointer, items)
|
|
kEndValueEx()
|
|
|
|
kBeginValueEx(k, xkXmlAttrBlock)
|
|
kAddField(xkXmlAttrBlock, kPointer, attrs)
|
|
kEndValueEx()
|
|
|
|
kFx(kStatus) kXml_Construct(kXml* xml, kAlloc allocator)
|
|
{
|
|
kAlloc alloc = kAlloc_Fallback(allocator);
|
|
kStatus status;
|
|
|
|
kCheck(kAlloc_GetObject(alloc, kTypeOf(kXml), xml));
|
|
|
|
if (!kSuccess(status = xkXml_Init(*xml, alloc)))
|
|
{
|
|
kAlloc_FreeRef(alloc, xml);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
kFx(kStatus) xkXml_Init(kXml xml, kAlloc allocator)
|
|
{
|
|
kObjR(kXml, xml);
|
|
|
|
kCheck(kObject_Init(xml, kTypeOf(kXml), allocator));
|
|
|
|
kCheck(kArrayList_Construct(&obj->pathParseBuffer, kTypeOf(kByte), 128, allocator));
|
|
kCheck(kArrayList_Construct(&obj->itemBlocks, kTypeOf(xkXmlItemBlock), 1, allocator));
|
|
kCheck(kArrayList_Construct(&obj->attrBlocks, kTypeOf(xkXmlAttrBlock), 1, allocator));
|
|
|
|
obj->itemBlockCount = xkXML_DEFAULT_ITEM_BLOCK_COUNT;
|
|
obj->attrBlockCount = xkXML_DEFAULT_ATTR_BLOCK_COUNT;
|
|
obj->root = kNULL;
|
|
obj->freeItems = kNULL;
|
|
obj->freeAttrs = kNULL;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkXml_VClone(kXml xml, kXml source, kAlloc valueAlloc, kObject context)
|
|
{
|
|
return kXml_Assign(xml, source);
|
|
}
|
|
|
|
kFx(kStatus) xkXml_VRelease(kXml xml)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
kXml_Clear(xml);
|
|
xkXml_FreeItemBlocks(xml);
|
|
xkXml_FreeAttrBlocks(xml);
|
|
|
|
kDestroyRef(&obj->itemBlocks);
|
|
kDestroyRef(&obj->attrBlocks);
|
|
kDestroyRef(&obj->pathParseBuffer);
|
|
|
|
kCheck(kObject_VRelease(xml));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkXml_WriteDat6V0(kXml xml, kSerializer serializer)
|
|
{
|
|
kString string = kNULL;
|
|
|
|
kTry
|
|
{
|
|
kTest(kString_Construct(&string, kNULL, kObject_Alloc(xml)));
|
|
|
|
kTest(kXml_ToString(xml, string));
|
|
|
|
kTest(kSerializer_WriteObject(serializer, string));
|
|
}
|
|
kFinally
|
|
{
|
|
kObject_Destroy(string);
|
|
kEndFinally();
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkXml_ReadDat6V0(kXml xml, kSerializer serializer)
|
|
{
|
|
kString string = kNULL;
|
|
|
|
kTry
|
|
{
|
|
kTest(kSerializer_ReadObject(serializer, &string, kObject_Alloc(xml)));
|
|
|
|
kTestTrue(kObject_Is(string, kTypeOf(kString)), kERROR_FORMAT);
|
|
|
|
kTest(kXml_FromString(xml, string));
|
|
}
|
|
kFinally
|
|
{
|
|
kObject_Destroy(string);
|
|
|
|
kEndFinally();
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_Compact(kXml xml)
|
|
{
|
|
kObj(kXml, xml);
|
|
kXml newXml;
|
|
kXmlClass* newObj;
|
|
|
|
// Make a clean copy, which is compact.
|
|
kCheck(kXml_Construct(&newXml, obj->base.alloc));
|
|
kCheck(kXml_Assign(newXml, xml));
|
|
newObj = xkXml_Cast(newXml);
|
|
|
|
// Copy over the guts
|
|
kCheck(kXml_Clear(xml));
|
|
kCheck(kArrayList_Assign(obj->attrBlocks, newObj->attrBlocks));
|
|
kCheck(kArrayList_Assign(obj->itemBlocks, newObj->itemBlocks));
|
|
obj->root = newObj->root;
|
|
obj->freeAttrs = newObj->freeAttrs;
|
|
obj->freeItems = newObj->freeItems;
|
|
|
|
// Remove references to copied data
|
|
kCheck(kArrayList_Clear(newObj->attrBlocks));
|
|
kCheck(kArrayList_Clear(newObj->itemBlocks));
|
|
newObj->root = kNULL;
|
|
newObj->freeAttrs = kNULL;
|
|
newObj->freeItems = kNULL;
|
|
|
|
kCheck(kObject_Destroy(newXml));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_Assign(kXml xml, kXml source)
|
|
{
|
|
kCheck(kXml_Clear(xml));
|
|
|
|
kCheck(kXml_CopyItem(xml, kNULL, kNULL, source, kXml_Root(source), kNULL));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_Read(kXml xml, kStream stream)
|
|
{
|
|
kObjR(kXml, xml);
|
|
xkXmlParseContext context;
|
|
|
|
context.isStream = kTRUE;
|
|
|
|
context.xml = xml;
|
|
context.buffer = kNULL;
|
|
context.currentItem = kNULL;
|
|
context.position = kNULL;
|
|
context.capacity = 0;
|
|
context.size = 0;
|
|
context.eof = kFALSE;
|
|
context.stream = stream;
|
|
|
|
kTry
|
|
{
|
|
context.capacity = 1024*10;
|
|
kTest(kAlloc_Get(obj->base.alloc, context.capacity, &context.buffer));
|
|
context.position = context.buffer;
|
|
|
|
kTest(xkXml_AdvanceParser(&context));
|
|
context.position = context.buffer;
|
|
kTest(xkXml_Parse(xml, &context));
|
|
}
|
|
kFinally
|
|
{
|
|
kAlloc_Free(obj->base.alloc, context.buffer);
|
|
context.buffer = kNULL;
|
|
kEndFinally();
|
|
}
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_Write(kXml xml, kStream stream)
|
|
{
|
|
kString str = kNULL;
|
|
|
|
kTry
|
|
{
|
|
kTest(kString_Construct(&str, kNULL, kNULL));
|
|
kTest(kXml_ToString(xml, str));
|
|
kTest(kStream_Write(stream, kString_Chars(str), kString_Length(str)));
|
|
}
|
|
kFinally
|
|
{
|
|
kObject_Destroy(str);
|
|
kEndFinally();
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_Load(kXml* xml, const kChar* fileName, kAlloc allocator)
|
|
{
|
|
kFile file = kNULL;
|
|
kXml result = kNULL;
|
|
|
|
kTry
|
|
{
|
|
kTest(kXml_Construct(&result, allocator));
|
|
|
|
kTest(kFile_Construct(&file, fileName, kFILE_MODE_READ, allocator));
|
|
kTest(kFile_SetReadBuffer(file, xkXML_FILE_READ_BUFFER_SIZE));
|
|
|
|
kTest(kXml_Read(result, file));
|
|
|
|
*xml = result;
|
|
result = kNULL;
|
|
}
|
|
kFinally
|
|
{
|
|
kObject_Destroy(result);
|
|
kObject_Destroy(file);
|
|
kEndFinally();
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_Save(kXml xml, const kChar* fileName)
|
|
{
|
|
kFile file = kNULL;
|
|
|
|
kTry
|
|
{
|
|
kTest(kFile_Construct(&file, fileName, kFILE_MODE_WRITE, kNULL));
|
|
kTest(kFile_SetWriteBuffer(file, xkXML_FILE_WRITE_BUFFER_SIZE));
|
|
|
|
kTest(kXml_Write(xml, file));
|
|
}
|
|
kFinally
|
|
{
|
|
kObject_Destroy(file);
|
|
kEndFinally();
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_FromString(kXml xml, kString str)
|
|
{
|
|
xkXmlParseContext context;
|
|
|
|
context.isStream = kFALSE;
|
|
context.buffer = (kChar*) kString_Chars(str); // isn't modified for non-stream
|
|
context.currentItem = kNULL;
|
|
context.position = kString_Chars(str);
|
|
context.xml = xml;
|
|
|
|
// unused
|
|
context.capacity = 0;
|
|
context.size = 0;
|
|
context.eof = kFALSE;
|
|
context.stream = kNULL;
|
|
|
|
kCheck(kXml_Clear(xml));
|
|
kCheck(xkXml_Parse(xml, &context));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_ToString(kXml xml, kString str)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
kCheck(kString_Import(str, "<", kFALSE));
|
|
|
|
kCheck(kString_Import(str, xkXML_DEFAULT_HEADER, kTRUE));
|
|
|
|
kCheck(kString_Import(str, ">", kTRUE));
|
|
kCheck(kString_Import(str, xkXML_NEW_LINE, kTRUE));
|
|
|
|
if (!kIsNull(obj->root))
|
|
{
|
|
kCheck(xkXml_ItemToString(xml, str, obj->root, 0));
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_FromText(kXml* xml, const kChar* str, kAlloc allocator)
|
|
{
|
|
kXml result = kNULL;
|
|
kStatus status;
|
|
xkXmlParseContext context;
|
|
|
|
context.isStream = kFALSE;
|
|
context.buffer = (kChar*)str; // isn't modified for non-stream
|
|
context.currentItem = kNULL;
|
|
context.position = str;
|
|
|
|
// unused
|
|
context.capacity = 0;
|
|
context.size = 0;
|
|
context.eof = kFALSE;
|
|
context.stream = kNULL;
|
|
|
|
kTry
|
|
{
|
|
kTest(kXml_Construct(&result, allocator));
|
|
context.xml = result;
|
|
kTest(xkXml_Parse(result, &context));
|
|
|
|
*xml = result;
|
|
}
|
|
kCatch(&status)
|
|
{
|
|
kObject_Destroy(result);
|
|
kEndCatch(status);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_SaveBytes(kXml xml, kByte** data, kSize* size, kAlloc allocator)
|
|
{
|
|
kAlloc alloc = kAlloc_Fallback(allocator);
|
|
kMemory stream = kNULL;
|
|
kByte* output = kNULL;
|
|
|
|
kTry
|
|
{
|
|
kTest(kMemory_Construct(&stream, kNULL));
|
|
kTest(kXml_Write(xml, stream));
|
|
|
|
kTest(kAlloc_Get(alloc, (kSize)kMemory_Length(stream), &output));
|
|
kTest(kMemCopy(output, kMemory_At(stream, 0), (kSize) kMemory_Length(stream)));
|
|
|
|
*size = (kSize) kMemory_Length(stream);
|
|
*data = output;
|
|
output = kNULL;
|
|
}
|
|
kFinally
|
|
{
|
|
kCheck(kObject_Destroy(stream));
|
|
kCheck(kAlloc_Free(alloc, output));
|
|
|
|
kEndFinally();
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_LoadBytes(kXml* xml, const kByte* data, kSize size, kAlloc allocator)
|
|
{
|
|
kAlloc alloc = kAlloc_Fallback(allocator);
|
|
kMemory stream = kNULL;
|
|
kXml output = kNULL;
|
|
|
|
kTry
|
|
{
|
|
kTest(kMemory_Construct(&stream, kNULL));
|
|
kTest(kMemory_Attach(stream, (void*)data, 0, size, size));
|
|
|
|
kTest(kXml_Construct(&output, alloc));
|
|
kTest(kXml_Read(output, stream));
|
|
|
|
*xml = output;
|
|
output = kNULL;
|
|
}
|
|
kFinally
|
|
{
|
|
kCheck(kObject_Destroy(stream));
|
|
kCheck(kObject_Destroy(output));
|
|
|
|
kEndFinally();
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_Clear(kXml xml)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (!kIsNull(obj->root))
|
|
{
|
|
kCheck(kXml_DeleteItem(xml, kXml_Root(xml)));
|
|
}
|
|
|
|
kCheck(xkXml_FreeItemBlocks(xml));
|
|
kCheck(xkXml_FreeAttrBlocks(xml));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kXmlItem) kXml_Root(kXml xml)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
return obj->root;
|
|
}
|
|
|
|
kFx(kStatus) kXml_AddItem(kXml xml, kXmlItem parent, const kChar* name, kXmlItem* item)
|
|
{
|
|
xkXmlItemClass* itemObj = kNULL;
|
|
|
|
kCheckArgs(xkXml_ValidateName(name));
|
|
|
|
kCheck(xkXml_AllocItem(xml, &itemObj));
|
|
kCheck(xkXml_LinkItemUnder(xml, kIsNull(parent) ? kNULL : xkXmlItem_Cast(parent), itemObj));
|
|
|
|
kCheck(xkXml_TextFieldSetString(xml, &itemObj->name, name));
|
|
kCheck(xkXml_TextFieldSetString(xml, &itemObj->value, ""));
|
|
|
|
if (!kIsNull(item))
|
|
{
|
|
*item = itemObj;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_InsertItem(kXml xml, kXmlItem next, const kChar* name, kXmlItem* item)
|
|
{
|
|
xkXmlItemClass* itemObj = kNULL;
|
|
|
|
kCheckArgs(!kIsNull(next) && xkXml_ValidateName(name));
|
|
|
|
kCheck(xkXml_AllocItem(xml, &itemObj));
|
|
kCheck(xkXml_LinkItemBefore(xml, xkXmlItem_Cast(next), itemObj));
|
|
|
|
kCheck(xkXml_TextFieldSetString(xml, &itemObj->name, name));
|
|
kCheck(xkXml_TextFieldSetString(xml, &itemObj->value, ""));
|
|
|
|
if (!kIsNull(item))
|
|
{
|
|
*item = itemObj;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_DeleteItem(kXml xml, kXmlItem item)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
|
|
kCheckArgs(!kIsNull(item));
|
|
|
|
kCheck(xkXml_UnlinkItem(xml, itemObj));
|
|
kCheck(xkXml_FreeItem(xml, itemObj));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_EnsureChildExists(kXml xml, kXmlItem parent, const kChar* path, kXmlItem* item)
|
|
{
|
|
const kChar* delim = kNULL;
|
|
xkXmlItemClass* foundItem = xkXmlItem_Cast(parent);
|
|
|
|
// for non-existent path items: if index is unspecified or 0, then the
|
|
// item is created. if index is greater than 0 then it is not created,
|
|
// an error is returned instead
|
|
|
|
do
|
|
{
|
|
kSize index;
|
|
kSize tokenSize;
|
|
xkXmlItemClass* tempItem;
|
|
|
|
delim = xkXml_FindDelimiter(path, xkXML_PATH_DELIMITERS);
|
|
tokenSize = (kSize) (delim - path);
|
|
index = xkXml_ParseNameSubstrIndex(path, tokenSize, &tokenSize);
|
|
|
|
if (!kSuccess(xkXml_SetPathParseBuffer(xml, path, tokenSize)))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
tempItem = xkXml_FindChildByName(xml, foundItem, xkXml_PathParseBuffer(xml), index);
|
|
if (kIsNull(tempItem) && index == 0)
|
|
{
|
|
kXmlItem newItem;
|
|
|
|
kCheck(kXml_AddItem(xml, foundItem, xkXml_PathParseBuffer(xml), &newItem));
|
|
tempItem = xkXmlItem_Cast(newItem);
|
|
}
|
|
foundItem = tempItem;
|
|
|
|
path = delim + 1;
|
|
}
|
|
while (*delim != '\0' && !kIsNull(foundItem));
|
|
|
|
if (kIsNull(foundItem))
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
if (!kIsNull(item))
|
|
{
|
|
*item = foundItem;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_DeleteChildren(kXml xml, kXmlItem item)
|
|
{
|
|
kXmlItem child = kNULL;
|
|
|
|
kCheckArgs(!kIsNull(item));
|
|
|
|
child = kXml_FirstChild(xml, item);
|
|
|
|
while (!kIsNull(child))
|
|
{
|
|
kCheck(kXml_DeleteItem(xml, child));
|
|
|
|
child = kXml_FirstChild(xml, item);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItemName(kXml xml, kXmlItem item, const kChar* name)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
|
|
kCheckArgs(!kIsNull(item));
|
|
|
|
return xkXml_TextFieldSetString(xml, &itemObj->name, name);
|
|
}
|
|
|
|
kFx(const kChar*) kXml_ItemName(kXml xml, kXmlItem item)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
|
|
kAssert(!kIsNull(item));
|
|
|
|
return xkXml_TextFieldString(xml, &itemObj->name);
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItemText(kXml xml, kXmlItem item, const kChar* str)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
|
|
kCheckArgs(!kIsNull(item));
|
|
|
|
return xkXml_TextFieldSetString(xml, &itemObj->value, str);
|
|
}
|
|
|
|
kFx(kStatus) kXml_ItemText(kXml xml, kXmlItem item, kChar* str, kSize capacity)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
const kChar* value;
|
|
|
|
kCheckArgs(!kIsNull(item));
|
|
|
|
value = xkXml_TextFieldString(xml, &itemObj->value);
|
|
|
|
if (capacity <= kStrLength(value))
|
|
{
|
|
return kERROR_INCOMPLETE;
|
|
}
|
|
|
|
kCheck(kStrCopy(str, capacity, value));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_ItemString(kXml xml, kXmlItem item, kString str)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
|
|
kCheckArgs(!kIsNull(item) && !kIsNull(str));
|
|
|
|
kCheck(kString_Import(str, xkXml_TextFieldString(xml, &itemObj->value), kFALSE));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetAttrText(kXml xml, kXmlItem item, const kChar* name, const kChar* str)
|
|
{
|
|
xkXmlAttrClass* attrObj;
|
|
|
|
kCheckArgs(!kIsNull(item));
|
|
|
|
attrObj = xkXml_FindAttrByName(xml, xkXmlItem_Cast(item), name);
|
|
|
|
if (kIsNull(attrObj))
|
|
{
|
|
kCheck(xkXml_AllocAttr(xml, &attrObj));
|
|
kCheck(xkXml_LinkAttr(xml, xkXmlItem_Cast(item), attrObj));
|
|
}
|
|
|
|
kCheck(xkXml_TextFieldSetString(xml, &attrObj->name, name));
|
|
kCheck(xkXml_TextFieldSetString(xml, &attrObj->value, str));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kBool) kXml_AttrExists(kXml xml, kXmlItem item, const kChar* name)
|
|
{
|
|
xkXmlAttrClass* attrObj;
|
|
|
|
kAssert(!kIsNull(item));
|
|
|
|
attrObj = xkXml_FindAttrByName(xml, xkXmlItem_Cast(item), name);
|
|
return (!kIsNull(attrObj)) ? kTRUE : kFALSE;
|
|
}
|
|
|
|
kFx(kSize) kXml_AttrCount(kXml xml, kXmlItem item)
|
|
{
|
|
xkXmlAttrClass* attrObj;
|
|
kSize count = 0;
|
|
|
|
kAssert(!kIsNull(item));
|
|
|
|
attrObj = xkXmlItem_Cast(item)->firstAttr;
|
|
while (attrObj != NULL)
|
|
{
|
|
++count;
|
|
attrObj = attrObj->next;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
kFx(const kChar*) kXml_AttrNameAt(kXml xml, kXmlItem item, kSize index)
|
|
{
|
|
xkXmlAttrClass* attrObj;
|
|
kSize i;
|
|
|
|
kAssert(!kIsNull(item));
|
|
|
|
attrObj = xkXmlItem_Cast(item)->firstAttr;
|
|
for (i = 0; (i < index) && (!kIsNull(attrObj)); ++i)
|
|
{
|
|
attrObj = attrObj->next;
|
|
}
|
|
|
|
return kIsNull(attrObj) ? kNULL : xkXml_TextFieldString(xml, &attrObj->name);
|
|
}
|
|
|
|
kFx(kStatus) kXml_DeleteAttr(kXml xml, kXmlItem item, const kChar* name)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
xkXmlAttrClass* attrObj;
|
|
xkXmlAttrClass* prevAttrObj;
|
|
|
|
kCheckArgs(!kIsNull(item));
|
|
|
|
attrObj = itemObj->firstAttr;
|
|
prevAttrObj = kNULL;
|
|
|
|
while (!kIsNull(attrObj))
|
|
{
|
|
if (kStrEquals(xkXml_TextFieldString(xml, &attrObj->name), name))
|
|
{
|
|
if (!kIsNull(prevAttrObj))
|
|
prevAttrObj->next = attrObj->next;
|
|
|
|
if (itemObj->firstAttr == attrObj)
|
|
itemObj->firstAttr = attrObj->next;
|
|
if (itemObj->lastAttr == attrObj)
|
|
itemObj->lastAttr = prevAttrObj;
|
|
|
|
return xkXml_FreeAttr(xml, attrObj);
|
|
}
|
|
prevAttrObj = attrObj;
|
|
attrObj = attrObj->next;
|
|
}
|
|
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
kFx(kStatus) kXml_DeleteAttrs(kXml xml, kXmlItem item)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
xkXmlAttrClass* attrObj = kNULL;
|
|
xkXmlAttrClass* nextAttrObj = kNULL;
|
|
|
|
kCheckArgs(!kIsNull(item));
|
|
|
|
attrObj = itemObj->firstAttr;
|
|
nextAttrObj = kNULL;
|
|
|
|
while (!kIsNull(attrObj))
|
|
{
|
|
nextAttrObj = attrObj->next;
|
|
|
|
kCheck(xkXml_FreeAttr(xml, attrObj));
|
|
|
|
attrObj = nextAttrObj;
|
|
}
|
|
|
|
itemObj->firstAttr = kNULL;
|
|
itemObj->lastAttr = kNULL;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_AttrString(kXml xml, kXmlItem item, const kChar* name, kString string)
|
|
{
|
|
xkXmlAttrClass* attrObj;
|
|
|
|
kCheckArgs(!kIsNull(item));
|
|
|
|
if (kIsNull(attrObj = xkXml_FindAttrByName(xml, xkXmlItem_Cast(item), name)))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
return kString_Set(string, xkXml_TextFieldString(xml, &attrObj->value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_AttrText(kXml xml, kXmlItem item, const kChar* name, kChar* str, kSize capacity)
|
|
{
|
|
xkXmlAttrClass* attrObj;
|
|
const kChar* value;
|
|
|
|
kCheckArgs(!kIsNull(item));
|
|
|
|
if (kIsNull(attrObj = xkXml_FindAttrByName(xml, xkXmlItem_Cast(item), name)))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
value = xkXml_TextFieldString(xml, &attrObj->value);
|
|
|
|
if (capacity <= kStrLength(value))
|
|
{
|
|
return kERROR_MEMORY;
|
|
}
|
|
|
|
kCheck(kStrCopy(str, capacity, value));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_FindRoot(kXml xml, kXmlItem* root)
|
|
{
|
|
return kIsNull(*root = kXml_Root(xml)) ? kERROR_FORMAT : kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_FindChild(kXml xml, kXmlItem parent, const kChar* path, kXmlItem* child)
|
|
{
|
|
return kIsNull(*child = kXml_Child(xml, parent, path)) ? kERROR_FORMAT : kOK;
|
|
}
|
|
|
|
kFx(kBool) kXml_ChildExists(kXml xml, kXmlItem parent, const kChar* path)
|
|
{
|
|
return kIsNull(kXml_Child(xml, parent, path)) ? kFALSE : kTRUE;
|
|
}
|
|
|
|
kFx(kSize) kXml_ChildCount(kXml xml, kXmlItem parent)
|
|
{
|
|
xkXmlItemClass* parentObj = xkXmlItem_Cast(parent);
|
|
xkXmlItemClass* childObj;
|
|
kSize count = 0;
|
|
|
|
kAssert(!kIsNull(parent));
|
|
|
|
childObj = parentObj->firstChild;
|
|
|
|
while (!kIsNull(childObj))
|
|
{
|
|
++count;
|
|
childObj = childObj->next;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
kFx(kXmlItem) kXml_ChildAt(kXml xml, kXmlItem parent, kSize index)
|
|
{
|
|
xkXmlItemClass* parentObj = xkXmlItem_Cast(parent);
|
|
xkXmlItemClass* childObj;
|
|
kSize i;
|
|
|
|
kAssert(!kIsNull(parent));
|
|
|
|
childObj = parentObj->firstChild;
|
|
|
|
for (i = 0; (i < index) && !kIsNull(childObj); ++i)
|
|
{
|
|
childObj = childObj->next;
|
|
}
|
|
|
|
return childObj;
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetChildText(kXml xml, kXmlItem parent, const kChar* path, const kChar* str)
|
|
{
|
|
kXmlItem item = kNULL;
|
|
|
|
kCheck(kXml_EnsureChildExists(xml, parent, path, &item));
|
|
|
|
kCheck(kXml_SetItemText(xml, item, str));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kXmlItem) kXml_Child(kXml xml, kXmlItem parent, const kChar* path)
|
|
{
|
|
const kChar* delim = kNULL;
|
|
xkXmlItemClass* foundItem = xkXmlItem_Cast(parent);
|
|
|
|
do
|
|
{
|
|
kSize index;
|
|
kSize tokenSize;
|
|
|
|
delim = xkXml_FindDelimiter(path, xkXML_PATH_DELIMITERS);
|
|
tokenSize = (kSize) (delim - path);
|
|
index = xkXml_ParseNameSubstrIndex(path, tokenSize, &tokenSize);
|
|
|
|
if (!kSuccess(xkXml_SetPathParseBuffer(xml, path, tokenSize)))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
foundItem = xkXml_FindChildByName(xml, foundItem, xkXml_PathParseBuffer(xml), index);
|
|
if (kIsNull(foundItem))
|
|
{
|
|
break;
|
|
}
|
|
path = delim + 1;
|
|
}
|
|
while (*delim != '\0');
|
|
|
|
return foundItem;
|
|
}
|
|
|
|
kFx(kXmlItem) kXml_FirstChild(kXml xml, kXmlItem parent)
|
|
{
|
|
xkXmlItemClass* parentObj = xkXmlItem_Cast(parent);
|
|
|
|
kAssert(!kIsNull(parent));
|
|
|
|
return parentObj->firstChild;
|
|
}
|
|
|
|
kFx(kXmlItem) kXml_LastChild(kXml xml, kXmlItem parent)
|
|
{
|
|
xkXmlItemClass* parentObj = xkXmlItem_Cast(parent);
|
|
|
|
kAssert(!kIsNull(parent));
|
|
|
|
return parentObj->lastChild;
|
|
}
|
|
|
|
kFx(kXmlItem) kXml_Parent(kXml xml, kXmlItem item)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
|
|
kAssert(!kIsNull(item));
|
|
|
|
return itemObj->parent;
|
|
}
|
|
|
|
kFx(kXmlItem) kXml_NextSibling(kXml xml, kXmlItem item)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
|
|
kAssert(!kIsNull(item));
|
|
|
|
return itemObj->next;
|
|
}
|
|
|
|
kFx(kXmlItem) kXml_PreviousSibling(kXml xml, kXmlItem item)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
|
|
kAssert(!kIsNull(item));
|
|
|
|
return itemObj->prev;
|
|
}
|
|
|
|
kFx(kStatus) kXml_Item16u(kXml xml, kXmlItem item, k16u* value)
|
|
{
|
|
return xkXml_Parse16u(xml, xkXml_ItemValue(xml, item), value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Item16s(kXml xml, kXmlItem item, k16s* value)
|
|
{
|
|
return xkXml_Parse16s(xml, xkXml_ItemValue(xml, item), value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Item32u(kXml xml, kXmlItem item, k32u* value)
|
|
{
|
|
return xkXml_Parse32u(xml, xkXml_ItemValue(xml, item), value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Item32s(kXml xml, kXmlItem item, k32s* value)
|
|
{
|
|
return xkXml_Parse32s(xml, xkXml_ItemValue(xml, item), value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_ItemBool(kXml xml, kXmlItem item, kBool* value)
|
|
{
|
|
return xkXml_ParseBool(xml, xkXml_ItemValue(xml, item), value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Item64u(kXml xml, kXmlItem item, k64u* value)
|
|
{
|
|
return xkXml_Parse64u(xml, xkXml_ItemValue(xml, item), value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Item64s(kXml xml, kXmlItem item, k64s* value)
|
|
{
|
|
return xkXml_Parse64s(xml, xkXml_ItemValue(xml, item), value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_ItemSize(kXml xml, kXmlItem item, kSize* value)
|
|
{
|
|
return xkXml_ParseSize(xml, xkXml_ItemValue(xml, item), value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Item32f(kXml xml, kXmlItem item, k32f* value)
|
|
{
|
|
return xkXml_Parse32f(xml, xkXml_ItemValue(xml, item), value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Item64f(kXml xml, kXmlItem item, k64f* value)
|
|
{
|
|
return xkXml_Parse64f(xml, xkXml_ItemValue(xml, item), value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItem16u(kXml xml, kXmlItem item, k16u value)
|
|
{
|
|
return kXml_SetItemText(xml, item, xkXml_Format16u(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItem16s(kXml xml, kXmlItem item, k16s value)
|
|
{
|
|
return kXml_SetItemText(xml, item, xkXml_Format16s(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItem32u(kXml xml, kXmlItem item, k32u value)
|
|
{
|
|
return kXml_SetItemText(xml, item, xkXml_Format32u(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItem32s(kXml xml, kXmlItem item, k32s value)
|
|
{
|
|
return kXml_SetItemText(xml, item, xkXml_Format32s(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItemBool(kXml xml, kXmlItem item, kBool value)
|
|
{
|
|
return kXml_SetItemText(xml, item, xkXml_FormatBool(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItem64u(kXml xml, kXmlItem item, k64u value)
|
|
{
|
|
return kXml_SetItemText(xml, item, xkXml_Format64u(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItem64s(kXml xml, kXmlItem item, k64s value)
|
|
{
|
|
return kXml_SetItemText(xml, item, xkXml_Format64s(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItemSize(kXml xml, kXmlItem item, kSize value)
|
|
{
|
|
return kXml_SetItemText(xml, item, xkXml_FormatSize(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItem32f(kXml xml, kXmlItem item, k32f value)
|
|
{
|
|
return kXml_SetItemText(xml, item, xkXml_Format32f(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetItem64f(kXml xml, kXmlItem item, k64f value)
|
|
{
|
|
return kXml_SetItemText(xml, item, xkXml_Format64f(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_Attr16u(kXml xml, kXmlItem item, const kChar* name, k16u* value)
|
|
{
|
|
const kChar* str = xkXml_AttrValue(xml, item, name);
|
|
|
|
if (kIsNull(str))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
return xkXml_Parse16u(xml, str, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Attr16s(kXml xml, kXmlItem item, const kChar* name, k16s* value)
|
|
{
|
|
const kChar* str = xkXml_AttrValue(xml, item, name);
|
|
|
|
if (kIsNull(str))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
return xkXml_Parse16s(xml, str, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Attr32u(kXml xml, kXmlItem item, const kChar* name, k32u* value)
|
|
{
|
|
const kChar* str = xkXml_AttrValue(xml, item, name);
|
|
|
|
if (kIsNull(str))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
return xkXml_Parse32u(xml, str, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Attr32s(kXml xml, kXmlItem item, const kChar* name, k32s* value)
|
|
{
|
|
const kChar* str = xkXml_AttrValue(xml, item, name);
|
|
|
|
if (kIsNull(str))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
return xkXml_Parse32s(xml, str, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_AttrBool(kXml xml, kXmlItem item, const kChar* name, kBool* value)
|
|
{
|
|
const kChar* str = xkXml_AttrValue(xml, item, name);
|
|
|
|
if (kIsNull(str))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
return xkXml_ParseBool(xml, str, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Attr64u(kXml xml, kXmlItem item, const kChar* name, k64u* value)
|
|
{
|
|
const kChar* str = xkXml_AttrValue(xml, item, name);
|
|
|
|
if (kIsNull(str))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
return xkXml_Parse64u(xml, str, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Attr64s(kXml xml, kXmlItem item, const kChar* name, k64s* value)
|
|
{
|
|
const kChar* str = xkXml_AttrValue(xml, item, name);
|
|
|
|
if (kIsNull(str))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
return xkXml_Parse64s(xml, str, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_AttrSize(kXml xml, kXmlItem item, const kChar* name, kSize* value)
|
|
{
|
|
const kChar* str = xkXml_AttrValue(xml, item, name);
|
|
|
|
if (kIsNull(str))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
return xkXml_ParseSize(xml, str, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Attr32f(kXml xml, kXmlItem item, const kChar* name, k32f* value)
|
|
{
|
|
const kChar* str = xkXml_AttrValue(xml, item, name);
|
|
|
|
if (kIsNull(str))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
return xkXml_Parse32f(xml, str, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Attr64f(kXml xml, kXmlItem item, const kChar* name, k64f* value)
|
|
{
|
|
const kChar* str = xkXml_AttrValue(xml, item, name);
|
|
|
|
if (kIsNull(str))
|
|
{
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
return xkXml_Parse64f(xml, str, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetAttr16u(kXml xml, kXmlItem item, const kChar* name, k16u value)
|
|
{
|
|
return kXml_SetAttrText(xml, item, name, xkXml_Format16u(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetAttr16s(kXml xml, kXmlItem item, const kChar* name, k16s value)
|
|
{
|
|
return kXml_SetAttrText(xml, item, name, xkXml_Format16s(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetAttr32u(kXml xml, kXmlItem item, const kChar* name, k32u value)
|
|
{
|
|
return kXml_SetAttrText(xml, item, name, xkXml_Format32u(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetAttr32s(kXml xml, kXmlItem item, const kChar* name, k32s value)
|
|
{
|
|
return kXml_SetAttrText(xml, item, name, xkXml_Format32s(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetAttrBool(kXml xml, kXmlItem item, const kChar* name, kBool value)
|
|
{
|
|
return kXml_SetAttrText(xml, item, name, xkXml_FormatBool(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetAttr64u(kXml xml, kXmlItem item, const kChar* name, k64u value)
|
|
{
|
|
return kXml_SetAttrText(xml, item, name, xkXml_Format64u(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetAttr64s(kXml xml, kXmlItem item, const kChar* name, k64s value)
|
|
{
|
|
return kXml_SetAttrText(xml, item, name, xkXml_Format64s(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetAttrSize(kXml xml, kXmlItem item, const kChar* name, kSize value)
|
|
{
|
|
return kXml_SetAttrText(xml, item, name, xkXml_FormatSize(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetAttr32f(kXml xml, kXmlItem item, const kChar* name, k32f value)
|
|
{
|
|
return kXml_SetAttrText(xml, item, name, xkXml_Format32f(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetAttr64f(kXml xml, kXmlItem item, const kChar* name, k64f value)
|
|
{
|
|
return kXml_SetAttrText(xml, item, name, xkXml_Format64f(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetChild16u(kXml xml, kXmlItem parent, const kChar* path, k16u value)
|
|
{
|
|
return kXml_SetChildText(xml, parent, path, xkXml_Format16u(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetChild16s(kXml xml, kXmlItem parent, const kChar* path, k16s value)
|
|
{
|
|
return kXml_SetChildText(xml, parent, path, xkXml_Format16s(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetChild32u(kXml xml, kXmlItem parent, const kChar* path, k32u value)
|
|
{
|
|
return kXml_SetChildText(xml, parent, path, xkXml_Format32u(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetChild32s(kXml xml, kXmlItem parent, const kChar* path, k32s value)
|
|
{
|
|
return kXml_SetChildText(xml, parent, path, xkXml_Format32s(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetChildBool(kXml xml, kXmlItem parent, const kChar* path, kBool value)
|
|
{
|
|
return kXml_SetChildText(xml, parent, path, xkXml_FormatBool(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetChild64u(kXml xml, kXmlItem parent, const kChar* path, k64u value)
|
|
{
|
|
return kXml_SetChildText(xml, parent, path, xkXml_Format64u(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetChild64s(kXml xml, kXmlItem parent, const kChar* path, k64s value)
|
|
{
|
|
return kXml_SetChildText(xml, parent, path, xkXml_Format64s(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetChildSize(kXml xml, kXmlItem parent, const kChar* path, kSize value)
|
|
{
|
|
return kXml_SetChildText(xml, parent, path, xkXml_Format64s(xml, (k64s)value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetChild32f(kXml xml, kXmlItem parent, const kChar* path, k32f value)
|
|
{
|
|
return kXml_SetChildText(xml, parent, path, xkXml_Format32f(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_SetChild64f(kXml xml, kXmlItem parent, const kChar* path, k64f value)
|
|
{
|
|
return kXml_SetChildText(xml, parent, path, xkXml_Format64f(xml, value));
|
|
}
|
|
|
|
kFx(kStatus) kXml_ChildString(kXml xml, kXmlItem parent, const kChar* path, kString str)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_ItemString(xml, child, str);
|
|
}
|
|
|
|
kFx(kStatus) kXml_ChildText(kXml xml, kXmlItem parent, const kChar* path, kChar* str, kSize capacity)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_ItemText(xml, child, str, capacity);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Child16u(kXml xml, kXmlItem parent, const kChar* path, k16u* value)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_Item16u(xml, child, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Child16s(kXml xml, kXmlItem parent, const kChar* path, k16s* value)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_Item16s(xml, child, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Child32u(kXml xml, kXmlItem parent, const kChar* path, k32u* value)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_Item32u(xml, child, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Child32s(kXml xml, kXmlItem parent, const kChar* path, k32s* value)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_Item32s(xml, child, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_ChildBool(kXml xml, kXmlItem parent, const kChar* path, kBool* value)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_ItemBool(xml, child, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Child64u(kXml xml, kXmlItem parent, const kChar* path, k64u* value)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_Item64u(xml, child, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Child64s(kXml xml, kXmlItem parent, const kChar* path, k64s* value)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_Item64s(xml, child, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_ChildSize(kXml xml, kXmlItem parent, const kChar* path, kSize* value)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_ItemSize(xml, child, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Child32f(kXml xml, kXmlItem parent, const kChar* path, k32f* value)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_Item32f(xml, child, value);
|
|
}
|
|
|
|
kFx(kStatus) kXml_Child64f(kXml xml, kXmlItem parent, const kChar* path, k64f* value)
|
|
{
|
|
kXmlItem child = kXml_Child(xml, parent, path);
|
|
|
|
kCheckArgs(!kIsNull(child));
|
|
|
|
return kXml_Item64f(xml, child, value);
|
|
}
|
|
|
|
// Private functions
|
|
|
|
kStatus xkXml_TextFieldInit(kXml xml, xkXmlTextField* field)
|
|
{
|
|
field->text[0] = '\0';
|
|
field->buffer = kNULL;
|
|
field->capacity = 0;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_TextFieldRelease(kXml xml, xkXmlTextField* field)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
kAlloc_Free(obj->base.alloc, field->buffer);
|
|
field->buffer = kNULL;
|
|
field->capacity = 0;
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_TextFieldReserve(kXml xml, xkXmlTextField* field, kSize size)
|
|
{
|
|
kObj(kXml, xml);
|
|
kSize bufferSize = size + 1;
|
|
|
|
if (bufferSize <= kCountOf(field->text))
|
|
{
|
|
if (!kIsNull(field->buffer))
|
|
{
|
|
kAlloc_Free(obj->base.alloc, field->buffer);
|
|
field->buffer = kNULL;
|
|
field->capacity = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!kIsNull(field->buffer) && field->capacity < bufferSize)
|
|
{
|
|
kAlloc_Free(obj->base.alloc, field->buffer);
|
|
field->buffer = kNULL;
|
|
field->capacity = 0;
|
|
}
|
|
|
|
if (kIsNull(field->buffer))
|
|
{
|
|
kCheck(kAlloc_Get(obj->base.alloc, bufferSize, &field->buffer));
|
|
field->capacity = bufferSize;
|
|
}
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_TextFieldSetString(kXml xml, xkXmlTextField* field, const kChar* str)
|
|
{
|
|
kObj(kXml, xml);
|
|
kSize bufferSize = kStrLength(str) + 1;
|
|
|
|
if (bufferSize <= kCountOf(field->text))
|
|
{
|
|
if (!kIsNull(field->buffer))
|
|
{
|
|
kAlloc_Free(obj->base.alloc, field->buffer);
|
|
field->buffer = kNULL;
|
|
field->capacity = 0;
|
|
}
|
|
kStrCopy(field->text, kCountOf(field->text), str);
|
|
}
|
|
else
|
|
{
|
|
kAlloc_Free(obj->base.alloc, field->buffer);
|
|
field->buffer = kNULL;
|
|
|
|
kCheck(kAlloc_Get(obj->base.alloc, bufferSize, &field->buffer));
|
|
field->capacity = bufferSize;
|
|
|
|
kStrCopy(field->buffer, field->capacity, str);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kChar* xkXml_TextFieldString(kXml xml, xkXmlTextField* field)
|
|
{
|
|
return (!kIsNull(field->buffer)) ? field->buffer : field->text;
|
|
}
|
|
|
|
kStatus xkXml_AllocItemBlock(kXml xml)
|
|
{
|
|
kObj(kXml, xml);
|
|
xkXmlItemBlock block;
|
|
kSize i;
|
|
|
|
kCheck(kAlloc_Get(obj->base.alloc, sizeof(xkXmlItemClass) * obj->itemBlockCount, &block.items));
|
|
|
|
for (i = 0; i < (obj->itemBlockCount - 1); ++i)
|
|
{
|
|
block.items[i].next = &block.items[i+1];
|
|
}
|
|
|
|
block.items[obj->itemBlockCount - 1].next = obj->freeItems;
|
|
obj->freeItems = &block.items[0];
|
|
|
|
kCheck(kArrayList_AddT(obj->itemBlocks, &block));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_AllocAttrBlock(kXml xml)
|
|
{
|
|
kObj(kXml, xml);
|
|
xkXmlAttrBlock block;
|
|
kSize i;
|
|
|
|
kCheck(kAlloc_Get(obj->base.alloc, sizeof(xkXmlAttrClass) * obj->attrBlockCount, &block.attrs));
|
|
|
|
for (i = 0; i < (obj->attrBlockCount - 1); ++i)
|
|
{
|
|
block.attrs[i].next = &block.attrs[i+1];
|
|
}
|
|
|
|
block.attrs[obj->attrBlockCount - 1].next = obj->freeAttrs;
|
|
obj->freeAttrs = &block.attrs[0];
|
|
|
|
kCheck(kArrayList_AddT(obj->attrBlocks, &block));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_FreeItemBlocks(kXml xml)
|
|
{
|
|
kObj(kXml, xml);
|
|
kSize i;
|
|
|
|
for (i = 0; i < kArrayList_Count(obj->itemBlocks); ++i)
|
|
{
|
|
xkXmlItemBlock* block = kArrayList_AtT(obj->itemBlocks, i, xkXmlItemBlock);
|
|
kCheck(kAlloc_Free(obj->base.alloc, block->items));
|
|
}
|
|
|
|
kCheck(kArrayList_Clear(obj->itemBlocks));
|
|
obj->freeItems = kNULL;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_FreeAttrBlocks(kXml xml)
|
|
{
|
|
kObj(kXml, xml);
|
|
kSize i;
|
|
|
|
for (i = 0; i < kArrayList_Count(obj->attrBlocks); ++i)
|
|
{
|
|
xkXmlAttrBlock* block = kArrayList_AtT(obj->attrBlocks, i, xkXmlAttrBlock);
|
|
kCheck(kAlloc_Free(obj->base.alloc, block->attrs));
|
|
}
|
|
|
|
kCheck(kArrayList_Clear(obj->attrBlocks));
|
|
obj->freeAttrs = kNULL;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_AllocItem(kXml xml, xkXmlItemClass** item)
|
|
{
|
|
kObj(kXml, xml);
|
|
xkXmlItemClass* newItem;
|
|
|
|
if (kIsNull(obj->freeItems))
|
|
{
|
|
kCheck(xkXml_AllocItemBlock(xml));
|
|
kCheck(!kIsNull(obj->freeItems));
|
|
}
|
|
|
|
newItem = obj->freeItems;
|
|
obj->freeItems = obj->freeItems->next;
|
|
|
|
kCheck(xkXml_TextFieldInit(xml, &newItem->name));
|
|
kCheck(xkXml_TextFieldInit(xml, &newItem->value));
|
|
|
|
newItem->firstAttr = newItem->lastAttr = kNULL;
|
|
newItem->parent = kNULL;
|
|
newItem->firstChild = newItem->lastChild = kNULL;
|
|
newItem->prev = newItem->next = kNULL;
|
|
|
|
*item = newItem;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_FreeItem(kXml xml, xkXmlItemClass* item)
|
|
{
|
|
kObj(kXml, xml);
|
|
xkXmlItemClass* child;
|
|
xkXmlAttrClass* attr;
|
|
|
|
for (attr = item->firstAttr; !kIsNull(attr); )
|
|
{
|
|
xkXmlAttrClass* next = attr->next;
|
|
kCheck(xkXml_FreeAttr(xml, attr));
|
|
attr = next;
|
|
}
|
|
|
|
for (child = item->firstChild; !kIsNull(child); )
|
|
{
|
|
xkXmlItemClass* next = child->next;
|
|
kCheck(xkXml_FreeItem(xml, child));
|
|
child = next;
|
|
}
|
|
|
|
kCheck(xkXml_TextFieldRelease(xml, &item->name));
|
|
kCheck(xkXml_TextFieldRelease(xml, &item->value));
|
|
|
|
item->next = obj->freeItems;
|
|
obj->freeItems = item;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_AllocAttr(kXml xml, xkXmlAttrClass** attr)
|
|
{
|
|
kObj(kXml, xml);
|
|
xkXmlAttrClass* newAttr;
|
|
|
|
if (kIsNull(obj->freeAttrs))
|
|
{
|
|
kCheck(xkXml_AllocAttrBlock(xml));
|
|
kCheck(!kIsNull(obj->freeAttrs));
|
|
}
|
|
|
|
newAttr = obj->freeAttrs;
|
|
obj->freeAttrs = newAttr->next;
|
|
|
|
kCheck(xkXml_TextFieldInit(xml, &newAttr->name));
|
|
kCheck(xkXml_TextFieldInit(xml, &newAttr->value));
|
|
newAttr->next = kNULL;
|
|
|
|
*attr = newAttr;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_FreeAttr(kXml xml, xkXmlAttrClass* attr)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
kCheck(xkXml_TextFieldRelease(xml, &attr->name));
|
|
kCheck(xkXml_TextFieldRelease(xml, &attr->value));
|
|
|
|
attr->next = obj->freeAttrs;
|
|
obj->freeAttrs = attr;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_LinkItemUnder(kXml xml, xkXmlItemClass* parent, xkXmlItemClass* item)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (kIsNull(parent))
|
|
{
|
|
if (!kIsNull(obj->root))
|
|
return kERROR_PARAMETER;
|
|
obj->root = item;
|
|
}
|
|
else
|
|
{
|
|
item->parent = parent;
|
|
if (parent->lastChild)
|
|
{
|
|
xkXmlItemClass* lastChild = parent->lastChild;
|
|
|
|
lastChild->next = item;
|
|
item->prev = parent->lastChild;
|
|
parent->lastChild = item;
|
|
}
|
|
else
|
|
{
|
|
parent->firstChild = item;
|
|
parent->lastChild = item;
|
|
}
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_LinkItemBefore(kXml xml, xkXmlItemClass* next, xkXmlItemClass* item)
|
|
{
|
|
item->parent = next->parent;
|
|
item->prev = next->prev;
|
|
item->next = next;
|
|
|
|
if (!kIsNull(next->prev))
|
|
{
|
|
next->prev->next = item;
|
|
}
|
|
|
|
next->prev = item;
|
|
|
|
if (!kIsNull(item->parent) && kIsNull(item->prev))
|
|
{
|
|
item->parent->firstChild = item;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_LinkItemAfter(kXml xml, xkXmlItemClass* prev, xkXmlItemClass* item)
|
|
{
|
|
item->parent = prev->parent;
|
|
item->prev = prev;
|
|
item->next = prev->next;
|
|
|
|
prev->next = item;
|
|
|
|
if (!kIsNull(prev->next))
|
|
{
|
|
prev->next->prev = item;
|
|
}
|
|
|
|
if (!kIsNull(item->parent) && kIsNull(item->next))
|
|
{
|
|
item->parent->lastChild = item;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_UnlinkItem(kXml xml, xkXmlItemClass* item)
|
|
{
|
|
kObj(kXml, xml);
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
xkXmlItemClass* parentObj = itemObj->parent;
|
|
|
|
if (!kIsNull(parentObj))
|
|
{
|
|
if (parentObj->firstChild == itemObj)
|
|
{
|
|
parentObj->firstChild = itemObj->next;
|
|
}
|
|
if (parentObj->lastChild == itemObj)
|
|
{
|
|
parentObj->lastChild = itemObj->prev;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
obj->root = kNULL;
|
|
}
|
|
|
|
if (!kIsNull(itemObj->prev))
|
|
{
|
|
itemObj->prev->next = itemObj->next;
|
|
}
|
|
|
|
if (!kIsNull(itemObj->next))
|
|
{
|
|
itemObj->next->prev = itemObj->prev;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_LinkAttr(kXml xml, xkXmlItemClass* item, xkXmlAttrClass* attr)
|
|
{
|
|
if (kIsNull(item->lastAttr))
|
|
{
|
|
item->firstAttr = attr;
|
|
item->lastAttr = attr;
|
|
}
|
|
else
|
|
{
|
|
item->lastAttr->next = attr;
|
|
item->lastAttr = attr;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
xkXmlAttrClass* xkXml_FindAttrByName(kXml xml, xkXmlItemClass* item, const kChar* name)
|
|
{
|
|
kObj(kXml, xml);
|
|
xkXmlAttrClass* attr = item->firstAttr;
|
|
|
|
while (!kIsNull(attr))
|
|
{
|
|
if (kStrEquals(xkXml_TextFieldString(obj, &attr->name), name))
|
|
{
|
|
return attr;
|
|
}
|
|
attr = attr->next;
|
|
}
|
|
|
|
return kNULL;
|
|
}
|
|
|
|
xkXmlItemClass* xkXml_FindChildByName(kXml xml, xkXmlItemClass* parent, const kChar* name, kSize index)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (kIsNull(parent))
|
|
{
|
|
// special case, item to find must be root and indexed 0
|
|
if (kIsNull(obj->root) || index != 0)
|
|
{
|
|
return kNULL;
|
|
}
|
|
if (!kStrEquals(xkXml_TextFieldString(xml, &obj->root->name), name))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return obj->root;
|
|
}
|
|
else
|
|
{
|
|
xkXmlItemClass* child = parent->firstChild;
|
|
kSize i = 0;
|
|
|
|
while (!kIsNull(child))
|
|
{
|
|
if (kStrEquals(xkXml_TextFieldString(xml, &child->name), name))
|
|
{
|
|
if (i >= index)
|
|
{
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
child = child->next;
|
|
}
|
|
|
|
return child;
|
|
}
|
|
}
|
|
|
|
kStatus xkXml_Substr(kXml xml, const kChar* input, kSize begin, kSize end, xkXmlTextField* str)
|
|
{
|
|
kSize size = end - begin;
|
|
kChar* cstr;
|
|
|
|
kCheck(xkXml_TextFieldReserve(xml, str, size));
|
|
cstr = xkXml_TextFieldString(xml, str);
|
|
|
|
kCheck(kMemCopy(cstr, &input[begin], size));
|
|
cstr[size] = '\0';
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_UnescapeSubstr(kXml xml, const kChar* input, kSize begin, kSize end, xkXmlTextField* str)
|
|
{
|
|
kChar* writer;
|
|
kSize i;
|
|
kBool found;
|
|
|
|
// a buffer that can hold the escaped string can also hold the unescaped string.
|
|
kCheck(xkXml_TextFieldReserve(xml, str, end - begin));
|
|
writer = xkXml_TextFieldString(xml, str);
|
|
|
|
while (begin != end)
|
|
{
|
|
if (input[begin] == '&')
|
|
{
|
|
const kChar* escBegin;
|
|
const kChar* escEnd;
|
|
const kChar* escPos;
|
|
|
|
++begin;
|
|
escBegin = &input[begin];
|
|
|
|
while (begin != end && input[begin] != ';')
|
|
{
|
|
++begin;
|
|
}
|
|
if (begin == end)
|
|
{
|
|
return kERROR;
|
|
}
|
|
escEnd = &input[begin];
|
|
++begin;
|
|
|
|
found = kFALSE;
|
|
|
|
for (i = 0; i < kCountOf(escSeqs); ++i)
|
|
{
|
|
if (strncmp(escSeqs[i].szSeq, escBegin, (kSize)(escEnd - escBegin)) == 0)
|
|
{
|
|
*(writer++) = escSeqs[i].character;
|
|
found = kTRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
*(writer++) = '&';
|
|
|
|
for (escPos = escBegin; escPos <= escEnd; ++escPos)
|
|
{
|
|
*(writer++) = *escPos;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*(writer++) = input[begin++];
|
|
}
|
|
}
|
|
|
|
*writer = '\0';
|
|
|
|
return kOK;
|
|
}
|
|
|
|
// This function returns a pointer to '\0' if a delimiter is not found
|
|
const kChar* xkXml_FindDelimiter(const kChar* str, const kChar* delims)
|
|
{
|
|
while (1)
|
|
{
|
|
const kChar* delimPtr;
|
|
|
|
if (*str == '\0')
|
|
{
|
|
return str;
|
|
}
|
|
|
|
for (delimPtr = delims; *delimPtr != '\0'; ++delimPtr)
|
|
{
|
|
if (*delimPtr == *str)
|
|
{
|
|
return str;
|
|
}
|
|
}
|
|
++str;
|
|
}
|
|
}
|
|
|
|
kSize xkXml_ParseNameSubstrIndex(const kChar* str, kSize size, kSize* adjustedSize)
|
|
{
|
|
const kChar* openBracket = kNULL;
|
|
const kChar* closeBracket = kNULL;
|
|
const kChar* ptr;
|
|
kChar buffer[128];
|
|
|
|
for (ptr = str; ptr < (str + size); ++ptr)
|
|
{
|
|
if (*ptr == '[')
|
|
{
|
|
openBracket = ptr;
|
|
}
|
|
else if (*ptr == ']')
|
|
{
|
|
closeBracket = ptr;
|
|
}
|
|
|
|
if (openBracket && closeBracket)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (openBracket && closeBracket && (openBracket < closeBracket))
|
|
{
|
|
kSize length = (kSize) (closeBracket - openBracket - 1);
|
|
|
|
if (adjustedSize)
|
|
{
|
|
*adjustedSize = (kSize) (openBracket - str);
|
|
}
|
|
|
|
if (length > (kCountOf(buffer) - 1))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
kMemCopy(buffer, openBracket + 1, length);
|
|
buffer[length] = '\0';
|
|
|
|
return (kSize) atoi(buffer);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
kStatus xkXml_SetPathParseBuffer(kXml xml, const kChar* text, kSize size)
|
|
{
|
|
kObj(kXml, xml);
|
|
kSize bufferSize = size + 1;
|
|
|
|
if (bufferSize > kArrayList_Capacity(obj->pathParseBuffer))
|
|
{
|
|
kCheck(kArrayList_Reserve(obj->pathParseBuffer, bufferSize));
|
|
}
|
|
|
|
kMemCopy(kArrayList_DataAt(obj->pathParseBuffer, 0), text, size);
|
|
*(kChar*)kArrayList_DataAt(obj->pathParseBuffer, (kSSize)size) = '\0';
|
|
|
|
return kOK;
|
|
}
|
|
|
|
const kChar* xkXml_PathParseBuffer(kXml xml)
|
|
{
|
|
kObj(kXml, xml);
|
|
return (kChar*)kArrayList_DataAt(obj->pathParseBuffer, 0);
|
|
}
|
|
|
|
kStatus xkXml_AddEscapedString(kXml xml, const kChar* in, kString out)
|
|
{
|
|
kSize i, j;
|
|
kSize len = kStrLength(in);
|
|
const kChar* start;
|
|
|
|
start = in;
|
|
for (i = 0; i < len; ++i)
|
|
{
|
|
kChar c = in[i];
|
|
|
|
for (j = 0; j < kCountOf(escSeqs); ++j)
|
|
{
|
|
if (escSeqs[j].character == c)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// found
|
|
if (j < kCountOf(escSeqs))
|
|
{
|
|
kCheck(kString_AddSubstring(out, start, 0u, (kSize) (&in[i] - start)));
|
|
start = &in[i] + 1;
|
|
kCheck(kString_Add(out, "&"));
|
|
kCheck(kString_Add(out, escSeqs[j].szSeq));
|
|
kCheck(kString_Add(out, ";"));
|
|
}
|
|
}
|
|
|
|
kCheck(kString_AddSubstring(out, start, 0u, (kSize)(in + len - start)));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kBool xkXml_ValidateName(const kChar *name)
|
|
{
|
|
kSize nameLen = kStrLength(name);
|
|
kSize i;
|
|
|
|
if (nameLen < 1)
|
|
{
|
|
return kFALSE;
|
|
}
|
|
|
|
if (!kChar_IsLetter(name[0]) && name[0] != '_' && name[0] != ':')
|
|
{
|
|
return kFALSE;
|
|
}
|
|
|
|
for (i = 1; i < nameLen; ++i)
|
|
{
|
|
if (!xkXml_IsNameChar(name[i]))
|
|
{
|
|
return kFALSE;
|
|
}
|
|
}
|
|
|
|
return kTRUE;
|
|
}
|
|
|
|
kBool xkXml_IsNameChar(kChar ch)
|
|
{
|
|
if (kChar_IsLetter(ch))
|
|
{
|
|
return kTRUE;
|
|
}
|
|
|
|
if (kChar_IsDigit(ch))
|
|
{
|
|
return kTRUE;
|
|
}
|
|
|
|
switch(ch)
|
|
{
|
|
case '.':
|
|
case '-':
|
|
case '_':
|
|
case ':':
|
|
return kTRUE;
|
|
|
|
default:
|
|
return kFALSE;
|
|
}
|
|
}
|
|
|
|
const kChar* xkXml_ItemValue(kXml xml, kXmlItem item)
|
|
{
|
|
xkXmlItemClass* itemObj = xkXmlItem_Cast(item);
|
|
|
|
kAssert(!kIsNull(item));
|
|
|
|
return xkXml_TextFieldString(xml, &itemObj->value);
|
|
}
|
|
|
|
const kChar* xkXml_AttrValue(kXml xml, kXmlItem item, const kChar* name)
|
|
{
|
|
xkXmlAttrClass* attr = xkXml_FindAttrByName(xml, xkXmlItem_Cast(item), name);
|
|
|
|
kAssert(!kIsNull(item));
|
|
|
|
if (kIsNull(attr))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return xkXml_TextFieldString(xml, &attr->value);
|
|
}
|
|
|
|
kStatus xkXml_ItemToString(kXml xml, kString str, xkXmlItemClass* item, kSize level)
|
|
{
|
|
xkXmlAttrClass* attr;
|
|
const kChar* value;
|
|
const kChar* name;
|
|
kSize freeSpace;
|
|
kSize i;
|
|
|
|
// Some rudimentary geometric buffer growth to improve performance - should be built into kString.
|
|
// This is very important for larger XML objects.
|
|
freeSpace = kString_Capacity(str) - kString_Length(str);
|
|
if (freeSpace < xkXML_STRING_INIT_SIZE)
|
|
{
|
|
kCheck(kString_Reserve(str, kMath_Round32u_(kString_Capacity(str) * 1.5)));
|
|
}
|
|
|
|
value = xkXml_TextFieldString(xml, &item->value);
|
|
name = xkXml_TextFieldString(xml, &item->name);
|
|
|
|
for (i = 0; i < level; ++i)
|
|
{
|
|
kCheck(kString_Import(str, xkXML_INDENTATION, kTRUE));
|
|
}
|
|
|
|
kCheck(kString_Import(str, "<", kTRUE));
|
|
kCheck(kString_Import(str, name, kTRUE));
|
|
|
|
attr = item->firstAttr;
|
|
while (!kIsNull(attr))
|
|
{
|
|
kCheck(kString_Import(str, "\x20", kTRUE));
|
|
kCheck(kString_Import(str, xkXml_TextFieldString(xml, &attr->name), kTRUE));
|
|
kCheck(kString_Import(str, "=\"", kTRUE));
|
|
kCheck(xkXml_AddEscapedString(xml, xkXml_TextFieldString(xml, &attr->value), str));
|
|
kCheck(kString_Import(str, "\"", kTRUE));
|
|
|
|
attr = attr->next;
|
|
}
|
|
|
|
if ((kStrLength(value) > 0) || !kIsNull(item->firstChild))
|
|
{
|
|
xkXmlItemClass* child = item->firstChild;
|
|
|
|
kCheck(kString_Import(str, ">", kTRUE));
|
|
if (!kIsNull(child))
|
|
{
|
|
kCheck(kString_Import(str, xkXML_NEW_LINE, kTRUE));
|
|
}
|
|
|
|
kCheck(xkXml_AddEscapedString(xml, value, str));
|
|
|
|
while (!kIsNull(child))
|
|
{
|
|
xkXml_ItemToString(xml, str, child, level + 1);
|
|
child = child->next;
|
|
}
|
|
|
|
if (!kIsNull(item->firstChild))
|
|
{
|
|
for (i = 0; i < level; ++i)
|
|
{
|
|
kCheck(kString_Import(str, xkXML_INDENTATION, kTRUE));
|
|
}
|
|
}
|
|
|
|
kCheck(kString_Import(str, "</", kTRUE));
|
|
kCheck(kString_Import(str, name, kTRUE));
|
|
}
|
|
else
|
|
{
|
|
kCheck(kString_Import(str, " /", kTRUE));
|
|
}
|
|
|
|
kCheck(kString_Import(str, ">", kTRUE));
|
|
kCheck(kString_Import(str, xkXML_NEW_LINE, kTRUE));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_CopyItem(kXml xml, kXmlItem parent, kXmlItem before, kXml srcXml, kXmlItem srcItem, kXmlItem* item)
|
|
{
|
|
kXmlItem newItem = kNULL;
|
|
xkXmlItemClass* srcItemObj = xkXmlItem_Cast(srcItem);
|
|
xkXmlAttrClass* attr;
|
|
kXmlItem child;
|
|
|
|
if (kIsNull(srcItem))
|
|
{
|
|
return kOK;
|
|
}
|
|
|
|
// Add item and set value
|
|
if (kIsNull(before))
|
|
{
|
|
kCheck(kXml_AddItem(xml, parent, kXml_ItemName(srcXml, srcItem), &newItem));
|
|
}
|
|
else
|
|
{
|
|
if (!kIsNull(parent) && kXml_Parent(xml, before) != parent)
|
|
{
|
|
return kERROR_PARAMETER;
|
|
}
|
|
|
|
kCheck(kXml_InsertItem(xml, before, kXml_ItemName(srcXml, srcItem), &newItem));
|
|
}
|
|
kCheck(kXml_SetItemText(xml, newItem, xkXml_ItemValue(srcXml, srcItem)));
|
|
|
|
// Copy attributes
|
|
attr = srcItemObj->firstAttr;
|
|
while (!kIsNull(attr))
|
|
{
|
|
kCheck(kXml_SetAttrText(xml, newItem,
|
|
xkXml_TextFieldString(srcXml, &attr->name),
|
|
xkXml_TextFieldString(srcXml, &attr->value)));
|
|
attr = attr->next;
|
|
}
|
|
|
|
// Copy children
|
|
child = kXml_FirstChild(srcXml, srcItem);
|
|
while (!kIsNull(child))
|
|
{
|
|
kCheck(kXml_CopyItem(xml, newItem, kNULL, srcXml, child, kNULL));
|
|
child = kXml_NextSibling(srcXml, child);
|
|
}
|
|
|
|
if (!kIsNull(item))
|
|
{
|
|
*item = newItem;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kXml_OverwriteItem(kXml xml, kXmlItem destItem, kXml srcXml, kXmlItem srcItem)
|
|
{
|
|
xkXmlItemClass* srcItemObj = xkXmlItem_Cast(srcItem);
|
|
xkXmlAttrClass* attr;
|
|
kXmlItem child;
|
|
|
|
kCheckArgs(!kIsNull(destItem) && !kIsNull(srcXml) && !kIsNull(srcItem));
|
|
|
|
kCheck(kXml_ClearItem(xml, destItem));
|
|
|
|
//copy name
|
|
kCheck(kXml_SetItemName(xml, destItem, kXml_ItemName(srcXml, srcItem)));
|
|
|
|
//copy attributes
|
|
attr = srcItemObj->firstAttr;
|
|
while (!kIsNull(attr))
|
|
{
|
|
kCheck(kXml_SetAttrText(xml, destItem, xkXml_TextFieldString(srcXml, &attr->name), xkXml_TextFieldString(srcXml, &attr->value)));
|
|
attr = attr->next;
|
|
}
|
|
|
|
//copy children
|
|
child = kXml_FirstChild(srcXml, srcItem);
|
|
while (!kIsNull(child))
|
|
{
|
|
kCheck(kXml_CopyItem(xml, destItem, kNULL, srcXml, child, kNULL));
|
|
child = kXml_NextSibling(srcXml, child);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
|
|
kFx(kStatus) kXml_ClearItem(kXml xml, kXmlItem item)
|
|
{
|
|
kCheckArgs(!kIsNull(item));
|
|
|
|
kCheck(kXml_DeleteChildren(xml, item));
|
|
kCheck(kXml_DeleteAttrs(xml, item));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
const kChar* xkXml_Format16u(kXml xml, k16u val)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (!kSuccess(k16u_Format(val, obj->formatBuffer, kCountOf(obj->formatBuffer))))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return obj->formatBuffer;
|
|
}
|
|
|
|
const kChar* xkXml_Format16s(kXml xml, k16s val)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (!kSuccess(k16s_Format(val, obj->formatBuffer, kCountOf(obj->formatBuffer))))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return obj->formatBuffer;
|
|
}
|
|
|
|
const kChar* xkXml_Format32u(kXml xml, k32u val)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (!kSuccess(k32u_Format(val, obj->formatBuffer, kCountOf(obj->formatBuffer))))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return obj->formatBuffer;
|
|
}
|
|
|
|
const kChar* xkXml_Format32s(kXml xml, k32s val)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (!kSuccess(k32s_Format(val, obj->formatBuffer, kCountOf(obj->formatBuffer))))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return obj->formatBuffer;
|
|
}
|
|
|
|
const kChar* xkXml_FormatBool(kXml xml, kBool val)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (!kSuccess(kBool_Format(val, obj->formatBuffer, kCountOf(obj->formatBuffer))))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return obj->formatBuffer;
|
|
}
|
|
|
|
const kChar* xkXml_Format64u(kXml xml, k64u val)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (!kSuccess(k64u_Format(val, obj->formatBuffer, kCountOf(obj->formatBuffer))))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return obj->formatBuffer;
|
|
}
|
|
|
|
const kChar* xkXml_Format64s(kXml xml, k64s val)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (!kSuccess(k64s_Format(val, obj->formatBuffer, kCountOf(obj->formatBuffer))))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return obj->formatBuffer;
|
|
}
|
|
|
|
const kChar* xkXml_FormatSize(kXml xml, kSize val)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (!kSuccess(kSize_Format(val, obj->formatBuffer, kCountOf(obj->formatBuffer))))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return obj->formatBuffer;
|
|
}
|
|
|
|
const kChar* xkXml_Format32f(kXml xml, k32f val)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (!kSuccess(xkStrFormat32f(val, obj->formatBuffer, kCountOf(obj->formatBuffer), k32F_DIGITS, kNULL)))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return obj->formatBuffer;
|
|
}
|
|
|
|
const kChar* xkXml_Format64f(kXml xml, k64f val)
|
|
{
|
|
kObj(kXml, xml);
|
|
|
|
if (!kSuccess(xkStrFormat64f(val, obj->formatBuffer, kCountOf(obj->formatBuffer), k64F_DIGITS, kNULL)))
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
return obj->formatBuffer;
|
|
}
|
|
|
|
kStatus xkXml_Parse16u(kXml xml, const kChar *string, k16u *val)
|
|
{
|
|
return k16u_Parse(val, string);
|
|
}
|
|
|
|
kStatus xkXml_Parse16s(kXml xml, const kChar *string, k16s *val)
|
|
{
|
|
return k16s_Parse(val, string);
|
|
}
|
|
|
|
kStatus xkXml_Parse32u(kXml xml, const kChar *string, k32u *val)
|
|
{
|
|
return k32u_Parse(val, string);
|
|
}
|
|
|
|
kStatus xkXml_Parse32s(kXml xml, const kChar *string, k32s *val)
|
|
{
|
|
return k32s_Parse(val, string);
|
|
}
|
|
|
|
kStatus xkXml_ParseBool(kXml xml, const kChar *string, kBool *val)
|
|
{
|
|
return kBool_Parse(val, string);
|
|
}
|
|
|
|
kStatus xkXml_Parse64u(kXml xml, const kChar *string, k64u *val)
|
|
{
|
|
return k64u_Parse(val, string);
|
|
}
|
|
|
|
kStatus xkXml_Parse64s(kXml xml, const kChar *string, k64s *val)
|
|
{
|
|
return k64s_Parse(val, string);
|
|
}
|
|
|
|
kStatus xkXml_ParseSize(kXml xml, const kChar *string, kSize *val)
|
|
{
|
|
return kSize_Parse(val, string);
|
|
}
|
|
|
|
kStatus xkXml_Parse32f(kXml xml, const kChar *string, k32f *val)
|
|
{
|
|
return k32f_Parse(val, string);
|
|
}
|
|
|
|
kStatus xkXml_Parse64f(kXml xml, const kChar *string, k64f *val)
|
|
{
|
|
return k64f_Parse(val, string);
|
|
}
|
|
|
|
kStatus xkXml_AdvanceParser(xkXmlParseContext* context)
|
|
{
|
|
kObj(kXml, context->xml);
|
|
|
|
if (context->isStream)
|
|
{
|
|
kChar ch;
|
|
kStatus status;
|
|
|
|
if (context->eof)
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
status = kStream_Read(context->stream, &ch, sizeof(kChar));
|
|
if (!kSuccess(status))
|
|
{
|
|
context->eof = kTRUE;
|
|
return kERROR;
|
|
}
|
|
|
|
if ((context->size + 1) > context->capacity)
|
|
{
|
|
kChar* newBuffer;
|
|
kSize position = (kSize)(context->position - context->buffer);
|
|
|
|
kCheck(kAlloc_Get(obj->base.alloc, context->capacity * 2, &newBuffer));
|
|
kCheck(kMemCopy(newBuffer, context->buffer, context->size));
|
|
kCheck(kAlloc_Free(obj->base.alloc, context->buffer));
|
|
context->buffer = kNULL;
|
|
context->buffer = newBuffer;
|
|
context->capacity = context->capacity * 2;
|
|
|
|
context->position = context->buffer + position;
|
|
}
|
|
context->buffer[context->size] = ch;
|
|
++context->size;
|
|
++context->position;
|
|
}
|
|
else
|
|
{
|
|
if (*context->position == '\0')
|
|
{
|
|
return kERROR;
|
|
}
|
|
++context->position;
|
|
}
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_SkipWhitespace(xkXmlParseContext* context)
|
|
{
|
|
while (kChar_IsSpace(*context->position))
|
|
{
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
}
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_SkipToken(xkXmlParseContext* context)
|
|
{
|
|
while (!kChar_IsSpace(*context->position) &&
|
|
*context->position != '<' &&
|
|
*context->position != '/' &&
|
|
*context->position != '>' &&
|
|
*context->position != '=')
|
|
{
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_Parse(kXml xml, xkXmlParseContext* context)
|
|
{
|
|
kObj(kXml, xml);
|
|
kSize valueBegin = 0;
|
|
kSize valueEnd = 0;
|
|
xkXmlTagType tagType = TAG_CLOSE;
|
|
|
|
kXml_Clear(xml);
|
|
|
|
while (*context->position != '\0')
|
|
{
|
|
if (*context->position == '<')
|
|
{
|
|
valueEnd = (kSize) (context->position - context->buffer);
|
|
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
kCheck(xkXml_SkipWhitespace(context));
|
|
|
|
// Comment/declaration parsing is a bit sloppy and probably not entirely spec-conforming but should do for our purposes.
|
|
// TODO: make this more rigorous
|
|
if (*context->position == '!')
|
|
{
|
|
kCheck(xkXml_ParseComment(xml, context));
|
|
}
|
|
else if (*context->position == '?')
|
|
{
|
|
kCheck(xkXml_ParseDeclaration(xml, context));
|
|
}
|
|
else
|
|
{
|
|
xkXmlTagType prevTagType = tagType;
|
|
xkXmlItemClass* prevNode = context->currentItem;
|
|
kBool done = kFALSE;
|
|
|
|
kCheck(xkXml_ParseTag(xml, context, &tagType));
|
|
|
|
// logic in this part needs some clean up
|
|
if (kIsNull(context->currentItem) && (tagType == TAG_CLOSE || tagType == TAG_EMPTY))
|
|
{
|
|
// root element is found (but don't stop yet, not necessarily completed)
|
|
done = kTRUE;
|
|
}
|
|
|
|
if (!done)
|
|
{
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
}
|
|
|
|
if (tagType == TAG_OPEN)
|
|
{
|
|
valueBegin = (kSize) (context->position - context->buffer);
|
|
}
|
|
else if (tagType == TAG_CLOSE)
|
|
{
|
|
// this detects a non-nested open/close pair. content within is considered as value.
|
|
// note: this doesn't support text nodes along side other nodes, but this isn't something
|
|
// we use. In fact there's no way on the API level to access this (only a single "value"
|
|
// per node, which isn't enough to represent text nodes).
|
|
// TODO: add this?
|
|
if (prevTagType == TAG_OPEN)
|
|
{
|
|
kCheck(xkXml_UnescapeSubstr(xml, context->buffer, valueBegin, valueEnd, &prevNode->value));
|
|
}
|
|
else
|
|
{
|
|
kCheck(xkXml_TextFieldSetString(xml, &prevNode->value, ""));
|
|
}
|
|
}
|
|
|
|
if (done)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (*context->position != '\0')
|
|
{
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!kIsNull(context->currentItem) || kIsNull(obj->root))
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_ParseTag(kXml xml, xkXmlParseContext* context, xkXmlTagType* tagType)
|
|
{
|
|
kSize tokenBegin;
|
|
kSize tokenEnd;
|
|
|
|
// read name
|
|
kCheck(xkXml_SkipWhitespace(context));
|
|
|
|
tokenBegin = (kSize) (context->position - context->buffer);
|
|
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
kCheck(xkXml_SkipToken(context));
|
|
|
|
if (!kChar_IsSpace(*context->position) && *context->position != '/' && *context->position != '>')
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
tokenEnd = (kSize) (context->position - context->buffer);
|
|
|
|
if (tokenBegin >= tokenEnd)
|
|
{
|
|
return kERROR; // zero length token is bad
|
|
}
|
|
|
|
if (context->buffer[tokenBegin] == '/') // closing item
|
|
{
|
|
if (strncmp(xkXml_TextFieldString(xml, &context->currentItem->name), &context->buffer[tokenBegin + 1], tokenEnd - tokenBegin - 1) != 0)
|
|
{
|
|
return kERROR; // mismatched tags
|
|
}
|
|
|
|
context->currentItem = context->currentItem->parent;
|
|
*tagType = TAG_CLOSE;
|
|
}
|
|
else // beginning item
|
|
{
|
|
xkXmlItemClass* parentItem;
|
|
|
|
parentItem = context->currentItem;
|
|
kCheck(xkXml_AllocItem(xml, &context->currentItem));
|
|
kCheck(xkXml_LinkItemUnder(xml, parentItem, context->currentItem));
|
|
|
|
kCheck(xkXml_Substr(xml, context->buffer, tokenBegin, tokenEnd, &context->currentItem->name));
|
|
kCheck(xkXml_ParseAttributes(xml, context));
|
|
|
|
// parse until end
|
|
kCheck(xkXml_SkipWhitespace(context));
|
|
|
|
if (*context->position == '/') // empty item
|
|
{
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
kCheck(xkXml_TextFieldSetString(xml, &context->currentItem->value, ""));
|
|
context->currentItem = parentItem;
|
|
*tagType = TAG_EMPTY;
|
|
}
|
|
else
|
|
{
|
|
*tagType = TAG_OPEN;
|
|
}
|
|
}
|
|
|
|
kCheck(xkXml_SkipWhitespace(context));
|
|
|
|
if (*context->position != '>')
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_ParseAttributes(kXml xml, xkXmlParseContext* context)
|
|
{
|
|
kSize tokenBegin; // use index because pointer can change during parsing
|
|
kSize tokenEnd;
|
|
xkXmlAttrClass* attr = kNULL;
|
|
|
|
while (kTRUE)
|
|
{
|
|
kCheck(xkXml_SkipWhitespace(context));
|
|
|
|
if (*context->position == '/' || *context->position == '>')
|
|
{
|
|
break;
|
|
}
|
|
tokenBegin = (kSize) (context->position - context->buffer);
|
|
kCheck(xkXml_SkipToken(context));
|
|
tokenEnd = (kSize) (context->position - context->buffer);
|
|
|
|
if (!kChar_IsSpace(context->buffer[tokenEnd]) &&
|
|
context->buffer[tokenEnd] != '/' &&
|
|
context->buffer[tokenEnd] != '>' &&
|
|
context->buffer[tokenEnd] != '=')
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
// allocate and insert
|
|
kCheck(xkXml_AllocAttr(xml, &attr));
|
|
kCheck(xkXml_LinkAttr(xml, context->currentItem, attr));
|
|
kCheck(xkXml_Substr(xml, context->buffer, tokenBegin, tokenEnd, &attr->name));
|
|
|
|
// parse value
|
|
kCheck(xkXml_SkipWhitespace(context));
|
|
|
|
if (*context->position == '=')
|
|
{
|
|
kChar quote;
|
|
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
kCheck(xkXml_SkipWhitespace(context));
|
|
|
|
quote = *context->position;
|
|
|
|
if (quote != '\'' && quote != '\"')
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
tokenBegin = (kSize) (context->position - context->buffer);
|
|
|
|
while (*context->position != '\0' && *context->position != quote)
|
|
{
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
}
|
|
|
|
tokenEnd = (kSize) (context->position - context->buffer);
|
|
|
|
if (context->buffer[tokenEnd] != quote)
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
kCheck(xkXml_Substr(xml, context->buffer, tokenBegin, tokenEnd, &attr->value));
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
}
|
|
else
|
|
{
|
|
kCheck(xkXml_TextFieldSetString(xml, &attr->value, ""));
|
|
}
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kStatus xkXml_ParseComment(kXml xml, xkXmlParseContext* context)
|
|
{
|
|
k32u state = 0;
|
|
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
|
|
if (*context->position != '-')
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
|
|
if (*context->position != '-')
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
|
|
while (*context->position != '\0')
|
|
{
|
|
kChar c = *context->position;
|
|
|
|
switch(state)
|
|
{
|
|
case 0:
|
|
if (c == '-')
|
|
state = 1;
|
|
break;
|
|
case 1:
|
|
if (c == '-')
|
|
state = 2;
|
|
else
|
|
state = 0;
|
|
break;
|
|
case 2:
|
|
if (c == '>')
|
|
{
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
return kOK;
|
|
}
|
|
else if (c != '-')
|
|
{
|
|
state = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
}
|
|
return kERROR;
|
|
}
|
|
|
|
kStatus xkXml_ParseDeclaration(kXml xml, xkXmlParseContext* context)
|
|
{
|
|
k32u state = 0;
|
|
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
|
|
while (*context->position != '\0')
|
|
{
|
|
switch(state)
|
|
{
|
|
case 0:
|
|
if (*context->position == '?')
|
|
{
|
|
state = 1;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (*context->position == '>')
|
|
{
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
return kOK;
|
|
}
|
|
else if (*context->position != '?')
|
|
{
|
|
state = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
kCheck(xkXml_AdvanceParser(context));
|
|
}
|
|
|
|
return kERROR;
|
|
}
|