mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 21:25:11 +00:00
[ADD/WIP] Baisic VCards, worst XEP-0393 parser
Blocks coming soon. Took me a day to get a system I remotely liked, and now I feel ashamed of that a bit. It's truly over...
This commit is contained in:
parent
c349b37f60
commit
4007232716
6 changed files with 409 additions and 12 deletions
291
src/XEP-0393.c
Normal file
291
src/XEP-0393.c
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
#include <XEP393.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Array.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static XEP393Element *
|
||||
CreateElementVessel(XEP393Element *parent, XEP393Type type)
|
||||
{
|
||||
XEP393Element *ret = Malloc(sizeof(*ret));
|
||||
ret->parent = parent;
|
||||
ret->type = type;
|
||||
ret->children = ArrayCreate();
|
||||
ret->text_data = NULL;
|
||||
|
||||
if (parent)
|
||||
{
|
||||
ArrayAdd(parent->children, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static void
|
||||
XEP393FreeElementBase(XEP393Element *element, bool unlink)
|
||||
{
|
||||
size_t i, len;
|
||||
if (!element)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
len = ArraySize(element->children);
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
XEP393FreeElement(ArrayGet(element->children, i));
|
||||
}
|
||||
|
||||
if (element->parent && unlink)
|
||||
{
|
||||
XEP393Element *parent = element->parent;
|
||||
len = ArraySize(parent->children);
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
XEP393Element *c = ArrayGet(parent->children, i);
|
||||
if (c == element)
|
||||
{
|
||||
ArrayDelete(parent->children, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayFree(element->children);
|
||||
Free(element->text_data);
|
||||
Free(element);
|
||||
}
|
||||
void
|
||||
XEP393FreeElement(XEP393Element *element)
|
||||
{
|
||||
XEP393FreeElementBase(element, false);
|
||||
}
|
||||
|
||||
typedef struct StrView {
|
||||
char *start;
|
||||
char *end;
|
||||
|
||||
bool heap_free;
|
||||
} StrView;
|
||||
#define ViewLength(v) ((size_t) ((v.end) - (v.start)))
|
||||
static char *
|
||||
StringifyView(StrView v)
|
||||
{
|
||||
char *r;
|
||||
size_t len;
|
||||
if (!v.start || v.start > v.end)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = ViewLength(v);
|
||||
r = Malloc(len + 1);
|
||||
memcpy(r, v.start, len);
|
||||
r[len] = '\0';
|
||||
|
||||
return r;
|
||||
}
|
||||
static StrView
|
||||
CreateStaticView(char *str)
|
||||
{
|
||||
StrView view = {
|
||||
.start = str,
|
||||
.end = str + strlen(str),
|
||||
.heap_free = false
|
||||
};
|
||||
|
||||
return view;
|
||||
}
|
||||
static bool
|
||||
IdentifySpan(char span_tag, StrView in, StrView *view)
|
||||
{
|
||||
size_t length;
|
||||
bool found = false;
|
||||
char prev = '\0';
|
||||
if (in.start >= in.end)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ViewLength(in) < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*in.start != span_tag || isspace(*(in.start + 1)))
|
||||
{
|
||||
/* The opening styling directive MUST NOT be followed
|
||||
* by a whitespace character */
|
||||
return false;
|
||||
}
|
||||
view->start = in.start + 1;
|
||||
in.start += 1;
|
||||
|
||||
for (length = 0; ViewLength(in) > 0; length++, in.start++)
|
||||
{
|
||||
if (*in.start == span_tag)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
prev = *in.start;
|
||||
}
|
||||
if (!found || !length || (prev && isspace(prev)))
|
||||
{
|
||||
/* the closing styling directive MUST NOT be preceeded
|
||||
* by a whitespace character. */
|
||||
return false;
|
||||
}
|
||||
|
||||
view->end = in.start;
|
||||
return true;
|
||||
}
|
||||
static void
|
||||
XEP393Decode(StrView view, XEP393Element *root)
|
||||
{
|
||||
StrView subview = view;
|
||||
StrView textview = view;
|
||||
XEP393Element *text, *span;
|
||||
size_t i;
|
||||
bool managed = false;
|
||||
|
||||
textview.end = subview.start;
|
||||
for (i = 0; subview.start < subview.end; subview.start++)
|
||||
{
|
||||
StrView span_view;
|
||||
managed = false;
|
||||
#define Spanify(xep_symbol) \
|
||||
managed = true; \
|
||||
textview.end = subview.start; \
|
||||
text = CreateElementVessel( \
|
||||
root, XEP393_TEXT \
|
||||
); \
|
||||
text->text_data = StringifyView(textview); \
|
||||
\
|
||||
/* Found a span. */ \
|
||||
span = CreateElementVessel( \
|
||||
root, xep_symbol \
|
||||
); \
|
||||
\
|
||||
XEP393Decode(span_view, span); \
|
||||
\
|
||||
/* Update subview */ \
|
||||
subview.start = span_view.end + 1; \
|
||||
\
|
||||
/* Update textview */ \
|
||||
textview.start = subview.start; \
|
||||
textview.end = subview.start
|
||||
if (IdentifySpan('_', subview, &span_view))
|
||||
{
|
||||
Spanify(XEP393_ITALIC);
|
||||
}
|
||||
else if (IdentifySpan('*', subview, &span_view))
|
||||
{
|
||||
Spanify(XEP393_EMPH);
|
||||
}
|
||||
else if (IdentifySpan('`', subview, &span_view))
|
||||
{
|
||||
Spanify(XEP393_MONO);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Text character: update end */
|
||||
textview.end = subview.start;
|
||||
}
|
||||
(void) i;
|
||||
}
|
||||
|
||||
if (!managed)
|
||||
{
|
||||
textview.end = subview.start;
|
||||
text = CreateElementVessel(
|
||||
root, XEP393_TEXT
|
||||
);
|
||||
text->text_data = StringifyView(textview);
|
||||
}
|
||||
}
|
||||
|
||||
XEP393Element *
|
||||
XEP393(char *message)
|
||||
{
|
||||
StrView view = CreateStaticView(message);
|
||||
XEP393Element *root = CreateElementVessel(NULL, XEP393_ROOT);
|
||||
|
||||
/* TODO: Parse blocks first, *then* spans. Considering the
|
||||
* current architecture, this shouldn't be too hard to integrate,
|
||||
* given how string views already manage boundaries, and elements
|
||||
* can already be used to contain blocks I think. */
|
||||
XEP393Decode(view, root);
|
||||
return root;
|
||||
}
|
||||
|
||||
#include <XML.h>
|
||||
|
||||
static void
|
||||
ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
||||
{
|
||||
XMLElement *head = xmlparent;
|
||||
size_t i;
|
||||
if (!element || !xmlparent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (element->type)
|
||||
{
|
||||
case XEP393_ITALIC:
|
||||
head = XMLCreateTag("i");
|
||||
XMLAddChild(xmlparent, head);
|
||||
break;
|
||||
case XEP393_EMPH:
|
||||
head = XMLCreateTag("strong");
|
||||
XMLAddChild(xmlparent, head);
|
||||
break;
|
||||
case XEP393_MONO:
|
||||
head = XMLCreateTag("code");
|
||||
XMLAddChild(xmlparent, head);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
if (ArraySize(element->children) == 0 && element->text_data)
|
||||
{
|
||||
XMLElement *text = XMLCreateText(element->text_data);
|
||||
XMLAddChild(head, text);
|
||||
}
|
||||
|
||||
for (i = 0; i < ArraySize(element->children); i++)
|
||||
{
|
||||
XEP393Element *sub = ArrayGet(element->children, i);
|
||||
ShoveXML(sub, head);
|
||||
}
|
||||
}
|
||||
|
||||
#include <StringStream.h>
|
||||
char *
|
||||
XEP393ToXMLString(XEP393Element *xepd)
|
||||
{
|
||||
XMLElement *root;
|
||||
|
||||
Stream *writer;
|
||||
char *ret = NULL;
|
||||
size_t i;
|
||||
if (!xepd)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root = XMLCreateTag("ROOT");
|
||||
ShoveXML(xepd, root);
|
||||
|
||||
writer = StrStreamWriter(&ret);
|
||||
for (i = 0; i < ArraySize(root->children); i++)
|
||||
{
|
||||
XMLEncode(writer, ArrayGet(root->children, i));
|
||||
}
|
||||
XMLFreeElement(root);
|
||||
StreamFlush(writer);
|
||||
StreamClose(writer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue