Parsee/src/XML/Parser.c
LDA 628a55fbca [MOD] Mess a bit with rich replies, add XEPS-TBD
This should be a nice list of future XEPs to be implemented, so that I
can keep track of these without having to keep all of these in my head.
2024-06-25 22:49:33 +02:00

198 lines
4.9 KiB
C

#include <XML.h>
#include <Cytoplasm/Log.h>
#include <Cytoplasm/Str.h>
#include <string.h>
XMLElement *
XMLCDecode(Stream *stream, bool autofree, bool html)
{
#define push(x) ArrayAdd(stack, x)
#define pop(x) ArrayDelete(stack, ArraySize(stack) - 1)
#define peek(x) ArrayGet(stack, ArraySize(stack) - 1)
XMLexer *lexer;
XMLEvent *event;
XMLElement *ret = NULL;
XMLElement *top;
char *key, *val;
size_t i;
Array *stack;
if (!stream)
{
return NULL;
}
lexer = XMLCreateLexer(stream, autofree);
stack = ArrayCreate();
while ((event = XMLCrank(lexer)) && (!ret || ArraySize(stack)))
{
bool flag = false;
switch (event->type)
{
case XML_LEXER_STARTELEM:
/* Create a new element that will populated. */
top = XMLCreateTag(event->element);
XMLAddChild(peek(), top);
while (HashMapIterate(event->attrs, &key, (void **) &val))
{
XMLAddAttr(top, key, val);
}
if (!ret)
{
/* If we didn't have any element before, this one is
* going to be our top of the stack */
ret = top;
}
/* HACK!!! */
if (!StrEquals(event->element, "br") || !html)
{
push(top);
}
break;
case XML_LEXER_ELEM:
/* Create a new element that will populated. */
top = XMLCreateTag(event->element);
XMLAddChild(peek(), top);
while (HashMapIterate(event->attrs, &key, (void **) &val))
{
XMLAddAttr(top, key, val);
}
if (!ret)
{
/* If we didn't have any element before, this one is
* going to be our top of the stack */
ret = top;
}
push(top);
/* Fallthrough */
case XML_LEXER_ENDELEM:
/* Pop out an element out of the DOM. */
pop();
if (!ArraySize(stack))
{
XMLFreeEvent(event);
event = NULL;
flag = true;
break;
}
break;
case XML_LEXER_DATA:
if (!peek())
{
break;
}
top = XMLCreateText(event->data);
XMLAddChild(peek(), top);
break;
default: break;
}
if (flag)
{
break;
}
XMLFreeEvent(event);
event = NULL;
}
XMLFreeEvent(event);
for (i = 0; i < ArraySize(stack); i++)
{
XMLElement *e = ArrayGet(stack, i);
if (e == ret)
{
continue;
}
//XMLFreeElement(e);
}
ArrayFree(stack);
XMLFreeLexer(lexer);
return ret;
}
void
XMLEncodeString(Stream *stream, char *data)
{
size_t i;
for (i = 0; i < strlen(data); i++)
{
char c = data[i];
if (c == '<')
{
StreamPrintf(stream, "&lt;");
continue;
}
else if (c == '>')
{
StreamPrintf(stream, "&gt;");
continue;
}
else if (c == '&')
{
StreamPrintf(stream, "&amp;");
continue;
}
else if (c == '\'')
{
StreamPrintf(stream, "&apos;");
continue;
}
else if (c == '"')
{
StreamPrintf(stream, "&quot;");
continue;
}
StreamPrintf(stream, "%c", c);
}
}
void
XMLEncodeTag(Stream *stream, XMLElement *element)
{
char *key, *val;
StreamPrintf(stream, "<%s", element->name);
while (HashMapIterate(element->attrs, &key, (void **) &val))
{
StreamPrintf(stream, " %s='", key);
XMLEncodeString(stream, val);
StreamPrintf(stream, "'");
}
if (ArraySize(element->children))
{
size_t i;
XMLElement *child;
StreamPrintf(stream, ">", key, val);
for (i = 0; i < ArraySize(element->children); i++)
{
child = ArrayGet(element->children, i);
XMLEncode(stream, child);
}
StreamPrintf(stream, "</%s>", element->name);
return;
}
StreamPrintf(stream, "/>");
}
void
XMLEncode(Stream *stream, XMLElement *element)
{
if (!stream || !element)
{
return;
}
switch (element->type)
{
case XML_ELEMENT_TAG:
XMLEncodeTag(stream, element);
return;
case XML_ELEMENT_DATA:
XMLEncodeString(stream, element->data);
return;
default:
/* TODO */
break;
}
}