1073 lines
23 KiB
C++
1073 lines
23 KiB
C++
//========================================================================
|
|
//
|
|
// Zoox.cc
|
|
//
|
|
//========================================================================
|
|
|
|
#include <aconf.h>
|
|
|
|
#ifdef USE_GCC_PRAGMAS
|
|
#pragma implementation
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "gmem.h"
|
|
#include "gmempp.h"
|
|
#include "GString.h"
|
|
#include "GList.h"
|
|
#include "GHash.h"
|
|
#include "Zoox.h"
|
|
|
|
//~ all of this code assumes the encoding is UTF-8 or ASCII or something
|
|
//~ similar (ISO-8859-*)
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
static char nameStartChar[256] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // 30
|
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 50
|
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 70
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // a0
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // b0
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // c0
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // d0
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // e0
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // f0
|
|
};
|
|
|
|
static char nameChar[256] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 20
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 30
|
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 50
|
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 70
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // a0
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // b0
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // c0
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // d0
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // e0
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // f0
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
ZxNode::ZxNode() {
|
|
next = NULL;
|
|
parent = NULL;
|
|
firstChild = lastChild = NULL;
|
|
}
|
|
|
|
ZxNode::~ZxNode() {
|
|
ZxNode *child;
|
|
|
|
while (firstChild) {
|
|
child = firstChild;
|
|
firstChild = firstChild->next;
|
|
delete child;
|
|
}
|
|
}
|
|
|
|
ZxNode *ZxNode::deleteChild(ZxNode *child) {
|
|
ZxNode *p1, *p2;
|
|
|
|
for (p1 = NULL, p2 = firstChild;
|
|
p2 && p2 != child;
|
|
p1 = p2, p2 = p2->next) ;
|
|
if (!p2) {
|
|
return NULL;
|
|
}
|
|
if (p1) {
|
|
p1->next = child->next;
|
|
} else {
|
|
firstChild = child->next;
|
|
}
|
|
child->parent = NULL;
|
|
child->next = NULL;
|
|
return child;
|
|
}
|
|
|
|
void ZxNode::appendChild(ZxNode *child) {
|
|
ZxNode *p1;
|
|
|
|
if (child->parent || child->next) {
|
|
return;
|
|
}
|
|
if (firstChild) {
|
|
for (p1 = firstChild; p1->next; p1 = p1->next) ;
|
|
p1->next = child;
|
|
} else {
|
|
firstChild = child;
|
|
}
|
|
child->parent = this;
|
|
}
|
|
|
|
void ZxNode::insertChildAfter(ZxNode *child, ZxNode *prev) {
|
|
if (child->parent || child->next || (prev && prev->parent != this)) {
|
|
return;
|
|
}
|
|
if (prev) {
|
|
child->next = prev->next;
|
|
prev->next = child;
|
|
} else {
|
|
child->next = firstChild;
|
|
firstChild = child;
|
|
}
|
|
child->parent = this;
|
|
}
|
|
|
|
ZxElement *ZxNode::findFirstElement(const char *type) {
|
|
ZxNode *child;
|
|
ZxElement *result;
|
|
|
|
if (isElement(type)) {
|
|
return (ZxElement *)this;
|
|
}
|
|
for (child = firstChild; child; child = child->next) {
|
|
if ((result = child->findFirstElement(type))) {
|
|
return result;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ZxElement *ZxNode::findFirstChildElement(const char *type) {
|
|
ZxNode *child;
|
|
|
|
for (child = firstChild; child; child = child->next) {
|
|
if (child->isElement(type)) {
|
|
return (ZxElement *)child;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
GList *ZxNode::findAllElements(const char *type) {
|
|
GList *results;
|
|
|
|
results = new GList();
|
|
findAllElements(type, results);
|
|
return results;
|
|
}
|
|
|
|
void ZxNode::findAllElements(const char *type, GList *results) {
|
|
ZxNode *child;
|
|
|
|
if (isElement(type)) {
|
|
results->append(this);
|
|
}
|
|
for (child = firstChild; child; child = child->next) {
|
|
child->findAllElements(type, results);
|
|
}
|
|
}
|
|
|
|
GList *ZxNode::findAllChildElements(const char *type) {
|
|
GList *results;
|
|
ZxNode *child;
|
|
|
|
results = new GList();
|
|
for (child = firstChild; child; child = child->next) {
|
|
if (child->isElement(type)) {
|
|
results->append(child);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
void ZxNode::addChild(ZxNode *child) {
|
|
if (lastChild) {
|
|
lastChild->next = child;
|
|
lastChild = child;
|
|
} else {
|
|
firstChild = lastChild = child;
|
|
}
|
|
child->parent = this;
|
|
child->next = NULL;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
ZxDoc::ZxDoc() {
|
|
xmlDecl = NULL;
|
|
docTypeDecl = NULL;
|
|
root = NULL;
|
|
}
|
|
|
|
ZxDoc *ZxDoc::loadMem(const char *data, Guint dataLen) {
|
|
ZxDoc *doc;
|
|
|
|
doc = new ZxDoc();
|
|
if (!doc->parse(data, dataLen)) {
|
|
delete doc;
|
|
return NULL;
|
|
}
|
|
return doc;
|
|
}
|
|
|
|
ZxDoc *ZxDoc::loadFile(const char *fileName) {
|
|
ZxDoc *doc;
|
|
FILE *f;
|
|
char *data;
|
|
Guint dataLen;
|
|
|
|
if (!(f = fopen(fileName, "rb"))) {
|
|
return NULL;
|
|
}
|
|
fseek(f, 0, SEEK_END);
|
|
dataLen = (Guint)ftell(f);
|
|
if (!dataLen) {
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
fseek(f, 0, SEEK_SET);
|
|
data = (char *)gmalloc(dataLen);
|
|
if (fread(data, 1, dataLen, f) != dataLen) {
|
|
fclose(f);
|
|
gfree(data);
|
|
return NULL;
|
|
}
|
|
fclose(f);
|
|
doc = loadMem(data, dataLen);
|
|
gfree(data);
|
|
return doc;
|
|
}
|
|
|
|
ZxDoc::~ZxDoc() {
|
|
}
|
|
|
|
static bool writeToFileFunc(void *stream, const char *data, int length) {
|
|
return (int)fwrite(data, 1, length, (FILE *)stream) == length;
|
|
}
|
|
|
|
bool ZxDoc::writeFile(const char *fileName) {
|
|
FILE *f;
|
|
|
|
if (!(f = fopen(fileName, "wb"))) {
|
|
return false;
|
|
}
|
|
write(&writeToFileFunc, f);
|
|
fclose(f);
|
|
return true;
|
|
}
|
|
|
|
void ZxDoc::addChild(ZxNode *node) {
|
|
if (node->isXMLDecl() && !xmlDecl) {
|
|
xmlDecl = (ZxXMLDecl *)node;
|
|
} else if (node->isDocTypeDecl() && !docTypeDecl) {
|
|
docTypeDecl = (ZxDocTypeDecl *)node;
|
|
} else if (node->isElement() && !root) {
|
|
root = (ZxElement *)node;
|
|
}
|
|
ZxNode::addChild(node);
|
|
}
|
|
|
|
bool ZxDoc::parse(const char *data, Guint dataLen) {
|
|
parsePtr = data;
|
|
parseEnd = data + dataLen;
|
|
|
|
parseSpace();
|
|
parseXMLDecl(this);
|
|
parseMisc(this);
|
|
parseDocTypeDecl(this);
|
|
parseMisc(this);
|
|
if (match("<")) {
|
|
parseElement(this);
|
|
}
|
|
parseMisc(this);
|
|
return root != NULL;
|
|
}
|
|
|
|
void ZxDoc::parseXMLDecl(ZxNode *par) {
|
|
GString *version, *encoding, *s;
|
|
bool standalone;
|
|
|
|
if (!match("<?xml")) {
|
|
return;
|
|
}
|
|
parsePtr += 5;
|
|
parseSpace();
|
|
|
|
// version
|
|
version = NULL;
|
|
if (match("version")) {
|
|
parsePtr += 7;
|
|
parseSpace();
|
|
if (match("=")) {
|
|
++parsePtr;
|
|
parseSpace();
|
|
version = parseQuotedString();
|
|
}
|
|
}
|
|
if (!version) {
|
|
version = new GString("1.0");
|
|
}
|
|
parseSpace();
|
|
|
|
// encoding
|
|
encoding = NULL;
|
|
if (match("encoding")) {
|
|
parsePtr += 8;
|
|
parseSpace();
|
|
if (match("=")) {
|
|
++parsePtr;
|
|
parseSpace();
|
|
encoding = parseQuotedString();
|
|
}
|
|
}
|
|
parseSpace();
|
|
|
|
// standalone
|
|
standalone = false;
|
|
if (match("standalone")) {
|
|
parsePtr += 10;
|
|
parseSpace();
|
|
if (match("=")) {
|
|
++parsePtr;
|
|
parseSpace();
|
|
s = parseQuotedString();
|
|
standalone = !s->cmp("yes");
|
|
delete s;
|
|
}
|
|
}
|
|
parseSpace();
|
|
|
|
if (match("?>")) {
|
|
parsePtr += 2;
|
|
}
|
|
|
|
par->addChild(new ZxXMLDecl(version, encoding, standalone));
|
|
}
|
|
|
|
//~ this just skips everything after the name
|
|
void ZxDoc::parseDocTypeDecl(ZxNode *par) {
|
|
GString *name;
|
|
int state;
|
|
char c, quote;
|
|
|
|
if (!match("<!DOCTYPE")) {
|
|
return;
|
|
}
|
|
parsePtr += 9;
|
|
parseSpace();
|
|
|
|
name = parseName();
|
|
parseSpace();
|
|
|
|
state = 0;
|
|
quote = '\0';
|
|
while (parsePtr < parseEnd && state < 4) {
|
|
c = *parsePtr++;
|
|
switch (state) {
|
|
case 0: // not in square brackets; not in quotes
|
|
if (c == '>') {
|
|
state = 4;
|
|
} else if (c == '"' || c == '\'') {
|
|
state = 1;
|
|
} else if (c == '[') {
|
|
state = 2;
|
|
}
|
|
break;
|
|
case 1: // not in square brackets; in quotes
|
|
if (c == quote) {
|
|
state = 0;
|
|
}
|
|
break;
|
|
case 2: // in square brackets; not in quotes
|
|
if (c == ']') {
|
|
state = 0;
|
|
} else if (c == '"' || c == '\'') {
|
|
state = 3;
|
|
}
|
|
break;
|
|
case 3: // in square brackets; in quotes
|
|
if (c == quote) {
|
|
state = 2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
par->addChild(new ZxDocTypeDecl(name));
|
|
}
|
|
|
|
// assumes match("<")
|
|
void ZxDoc::parseElement(ZxNode *par) {
|
|
GString *type;
|
|
ZxElement *elem;
|
|
ZxAttr *attr;
|
|
|
|
++parsePtr;
|
|
type = parseName();
|
|
elem = new ZxElement(type);
|
|
parseSpace();
|
|
while ((attr = parseAttr())) {
|
|
elem->addAttr(attr);
|
|
parseSpace();
|
|
}
|
|
if (match("/>")) {
|
|
parsePtr += 2;
|
|
} else if (match(">")) {
|
|
++parsePtr;
|
|
parseContent(elem);
|
|
}
|
|
par->addChild(elem);
|
|
}
|
|
|
|
ZxAttr *ZxDoc::parseAttr() {
|
|
GString *name, *value;
|
|
const char *start;
|
|
char quote, c;
|
|
unsigned int x;
|
|
int n;
|
|
|
|
name = parseName();
|
|
parseSpace();
|
|
if (!match("=")) {
|
|
delete name;
|
|
return NULL;
|
|
}
|
|
++parsePtr;
|
|
parseSpace();
|
|
if (!(parsePtr < parseEnd && (*parsePtr == '"' || *parsePtr == '\''))) {
|
|
delete name;
|
|
return NULL;
|
|
}
|
|
quote = *parsePtr++;
|
|
value = new GString();
|
|
while (parsePtr < parseEnd && *parsePtr != quote) {
|
|
if (*parsePtr == '&') {
|
|
++parsePtr;
|
|
if (parsePtr < parseEnd && *parsePtr == '#') {
|
|
++parsePtr;
|
|
if (parsePtr < parseEnd && *parsePtr == 'x') {
|
|
++parsePtr;
|
|
x = 0;
|
|
while (parsePtr < parseEnd) {
|
|
c = *parsePtr;
|
|
if (c >= '0' && c <= '9') {
|
|
x = (x << 4) + (c - '0');
|
|
} else if (c >= 'a' && c <= 'f') {
|
|
x = (x << 4) + (10 + c - 'a');
|
|
} else if (c >= 'A' && c <= 'F') {
|
|
x = (x << 4) + (10 + c - 'A');
|
|
} else {
|
|
break;
|
|
}
|
|
++parsePtr;
|
|
}
|
|
if (parsePtr < parseEnd && *parsePtr == ';') {
|
|
++parsePtr;
|
|
}
|
|
appendUTF8(value, x);
|
|
} else {
|
|
x = 0;
|
|
while (parsePtr < parseEnd) {
|
|
c = *parsePtr;
|
|
if (c >= '0' && c <= '9') {
|
|
x = x * 10 + (c - '0');
|
|
} else {
|
|
break;
|
|
}
|
|
++parsePtr;
|
|
}
|
|
if (parsePtr < parseEnd && *parsePtr == ';') {
|
|
++parsePtr;
|
|
}
|
|
appendUTF8(value, x);
|
|
}
|
|
} else {
|
|
start = parsePtr;
|
|
for (++parsePtr;
|
|
parsePtr < parseEnd && *parsePtr != ';' &&
|
|
*parsePtr != quote && *parsePtr != '&';
|
|
++parsePtr) ;
|
|
n = (int)(parsePtr - start);
|
|
if (parsePtr < parseEnd && *parsePtr == ';') {
|
|
++parsePtr;
|
|
}
|
|
if (n == 2 && !strncmp(start, "lt", 2)) {
|
|
value->append('<');
|
|
} else if (n == 2 && !strncmp(start, "gt", 2)) {
|
|
value->append('>');
|
|
} else if (n == 3 && !strncmp(start, "amp", 3)) {
|
|
value->append('&');
|
|
} else if (n == 4 && !strncmp(start, "apos", 4)) {
|
|
value->append('\'');
|
|
} else if (n == 4 && !strncmp(start, "quot", 4)) {
|
|
value->append('"');
|
|
} else {
|
|
value->append(start - 1, (int)(parsePtr - start) + 1);
|
|
}
|
|
}
|
|
} else {
|
|
start = parsePtr;
|
|
for (++parsePtr;
|
|
parsePtr < parseEnd && *parsePtr != quote && *parsePtr != '&';
|
|
++parsePtr) ;
|
|
value->append(start, (int)(parsePtr - start));
|
|
}
|
|
}
|
|
if (parsePtr < parseEnd && *parsePtr == quote) {
|
|
++parsePtr;
|
|
}
|
|
return new ZxAttr(name, value);
|
|
}
|
|
|
|
// this consumes the end tag
|
|
void ZxDoc::parseContent(ZxElement *par) {
|
|
GString *endType;
|
|
|
|
endType = (new GString("</"))->append(par->getType());
|
|
|
|
while (parsePtr < parseEnd) {
|
|
if (match(endType->getCString())) {
|
|
parsePtr += endType->getLength();
|
|
parseSpace();
|
|
if (match(">")) {
|
|
++parsePtr;
|
|
}
|
|
break;
|
|
} else if (match("<?")) {
|
|
parsePI(par);
|
|
} else if (match("<![CDATA[")) {
|
|
parseCDSect(par);
|
|
} else if (match("<!--")) {
|
|
parseComment(par);
|
|
} else if (match("<")) {
|
|
parseElement(par);
|
|
} else {
|
|
parseCharData(par);
|
|
}
|
|
}
|
|
|
|
delete endType;
|
|
}
|
|
|
|
void ZxDoc::parseCharData(ZxElement *par) {
|
|
GString *data;
|
|
const char *start;
|
|
char c;
|
|
unsigned int x;
|
|
int n;
|
|
|
|
data = new GString();
|
|
while (parsePtr < parseEnd && *parsePtr != '<') {
|
|
if (*parsePtr == '&') {
|
|
++parsePtr;
|
|
if (parsePtr < parseEnd && *parsePtr == '#') {
|
|
++parsePtr;
|
|
if (parsePtr < parseEnd && *parsePtr == 'x') {
|
|
++parsePtr;
|
|
x = 0;
|
|
while (parsePtr < parseEnd) {
|
|
c = *parsePtr;
|
|
if (c >= '0' && c <= '9') {
|
|
x = (x << 4) + (c - '0');
|
|
} else if (c >= 'a' && c <= 'f') {
|
|
x = (x << 4) + (10 + c - 'a');
|
|
} else if (c >= 'A' && c <= 'F') {
|
|
x = (x << 4) + (10 + c - 'A');
|
|
} else {
|
|
break;
|
|
}
|
|
++parsePtr;
|
|
}
|
|
if (parsePtr < parseEnd && *parsePtr == ';') {
|
|
++parsePtr;
|
|
}
|
|
appendUTF8(data, x);
|
|
} else {
|
|
x = 0;
|
|
while (parsePtr < parseEnd) {
|
|
c = *parsePtr;
|
|
if (c >= '0' && c <= '9') {
|
|
x = x * 10 + (c - '0');
|
|
} else {
|
|
break;
|
|
}
|
|
++parsePtr;
|
|
}
|
|
if (parsePtr < parseEnd && *parsePtr == ';') {
|
|
++parsePtr;
|
|
}
|
|
appendUTF8(data, x);
|
|
}
|
|
} else {
|
|
start = parsePtr;
|
|
for (++parsePtr;
|
|
parsePtr < parseEnd && *parsePtr != ';' &&
|
|
*parsePtr != '<' && *parsePtr != '&';
|
|
++parsePtr) ;
|
|
n = (int)(parsePtr - start);
|
|
if (parsePtr < parseEnd && *parsePtr == ';') {
|
|
++parsePtr;
|
|
}
|
|
if (n == 2 && !strncmp(start, "lt", 2)) {
|
|
data->append('<');
|
|
} else if (n == 2 && !strncmp(start, "gt", 2)) {
|
|
data->append('>');
|
|
} else if (n == 3 && !strncmp(start, "amp", 3)) {
|
|
data->append('&');
|
|
} else if (n == 4 && !strncmp(start, "apos", 4)) {
|
|
data->append('\'');
|
|
} else if (n == 4 && !strncmp(start, "quot", 4)) {
|
|
data->append('"');
|
|
} else {
|
|
data->append(start - 1, (int)(parsePtr - start) + 1);
|
|
}
|
|
}
|
|
} else {
|
|
start = parsePtr;
|
|
for (++parsePtr;
|
|
parsePtr < parseEnd && *parsePtr != '<' && *parsePtr != '&';
|
|
++parsePtr) ;
|
|
data->append(start, (int)(parsePtr - start));
|
|
}
|
|
}
|
|
par->addChild(new ZxCharData(data, true));
|
|
}
|
|
|
|
void ZxDoc::appendUTF8(GString *s, unsigned int c) {
|
|
if (c <= 0x7f) {
|
|
s->append((char)c);
|
|
} else if (c <= 0x7ff) {
|
|
s->append((char)(0xc0 + (c >> 6)));
|
|
s->append((char)(0x80 + (c & 0x3f)));
|
|
} else if (c <= 0xffff) {
|
|
s->append((char)(0xe0 + (c >> 12)));
|
|
s->append((char)(0x80 + ((c >> 6) & 0x3f)));
|
|
s->append((char)(0x80 + (c & 0x3f)));
|
|
} else if (c <= 0x1fffff) {
|
|
s->append((char)(0xf0 + (c >> 18)));
|
|
s->append((char)(0x80 + ((c >> 12) & 0x3f)));
|
|
s->append((char)(0x80 + ((c >> 6) & 0x3f)));
|
|
s->append((char)(0x80 + (c & 0x3f)));
|
|
} else if (c <= 0x3ffffff) {
|
|
s->append((char)(0xf8 + (c >> 24)));
|
|
s->append((char)(0x80 + ((c >> 18) & 0x3f)));
|
|
s->append((char)(0x80 + ((c >> 12) & 0x3f)));
|
|
s->append((char)(0x80 + ((c >> 6) & 0x3f)));
|
|
s->append((char)(0x80 + (c & 0x3f)));
|
|
} else if (c <= 0x7fffffff) {
|
|
s->append((char)(0xfc + (c >> 30)));
|
|
s->append((char)(0x80 + ((c >> 24) & 0x3f)));
|
|
s->append((char)(0x80 + ((c >> 18) & 0x3f)));
|
|
s->append((char)(0x80 + ((c >> 12) & 0x3f)));
|
|
s->append((char)(0x80 + ((c >> 6) & 0x3f)));
|
|
s->append((char)(0x80 + (c & 0x3f)));
|
|
}
|
|
}
|
|
|
|
// assumes match("<![CDATA[")
|
|
void ZxDoc::parseCDSect(ZxNode *par) {
|
|
const char *start;
|
|
|
|
parsePtr += 9;
|
|
start = parsePtr;
|
|
while (parsePtr < parseEnd - 3) {
|
|
if (!strncmp(parsePtr, "]]>", 3)) {
|
|
par->addChild(new ZxCharData(new GString(start, (int)(parsePtr - start)),
|
|
false));
|
|
parsePtr += 3;
|
|
return;
|
|
}
|
|
++parsePtr;
|
|
}
|
|
parsePtr = parseEnd;
|
|
par->addChild(new ZxCharData(new GString(start, (int)(parsePtr - start)),
|
|
false));
|
|
}
|
|
|
|
void ZxDoc::parseMisc(ZxNode *par) {
|
|
while (1) {
|
|
if (match("<!--")) {
|
|
parseComment(par);
|
|
} else if (match("<?")) {
|
|
parsePI(par);
|
|
} else if (parsePtr < parseEnd && (*parsePtr == '\x20' ||
|
|
*parsePtr == '\x09' ||
|
|
*parsePtr == '\x0d' ||
|
|
*parsePtr == '\x0a')) {
|
|
++parsePtr;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// assumes match("<!--")
|
|
void ZxDoc::parseComment(ZxNode *par) {
|
|
const char *start;
|
|
|
|
parsePtr += 4;
|
|
start = parsePtr;
|
|
while (parsePtr <= parseEnd - 3) {
|
|
if (!strncmp(parsePtr, "-->", 3)) {
|
|
par->addChild(new ZxComment(new GString(start, (int)(parsePtr - start))));
|
|
parsePtr += 3;
|
|
return;
|
|
}
|
|
++parsePtr;
|
|
}
|
|
parsePtr = parseEnd;
|
|
}
|
|
|
|
// assumes match("<?")
|
|
void ZxDoc::parsePI(ZxNode *par) {
|
|
GString *target;
|
|
const char *start;
|
|
|
|
parsePtr += 2;
|
|
target = parseName();
|
|
parseSpace();
|
|
start = parsePtr;
|
|
while (parsePtr <= parseEnd - 2) {
|
|
if (!strncmp(parsePtr, "?>", 2)) {
|
|
par->addChild(new ZxPI(target, new GString(start,
|
|
(int)(parsePtr - start))));
|
|
parsePtr += 2;
|
|
return;
|
|
}
|
|
++parsePtr;
|
|
}
|
|
parsePtr = parseEnd;
|
|
par->addChild(new ZxPI(target, new GString(start, (int)(parsePtr - start))));
|
|
}
|
|
|
|
//~ this accepts all chars >= 0x80
|
|
//~ this doesn't check for properly-formed UTF-8
|
|
GString *ZxDoc::parseName() {
|
|
GString *name;
|
|
|
|
name = new GString();
|
|
if (parsePtr < parseEnd && nameStartChar[*parsePtr & 0xff]) {
|
|
name->append(*parsePtr++);
|
|
while (parsePtr < parseEnd && nameChar[*parsePtr & 0xff]) {
|
|
name->append(*parsePtr++);
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
GString *ZxDoc::parseQuotedString() {
|
|
GString *s;
|
|
const char *start;
|
|
char quote;
|
|
|
|
if (parsePtr < parseEnd && (*parsePtr == '"' || *parsePtr == '\'')) {
|
|
quote = *parsePtr++;
|
|
start = parsePtr;
|
|
while (parsePtr < parseEnd && *parsePtr != quote) {
|
|
++parsePtr;
|
|
}
|
|
s = new GString(start, (int)(parsePtr - start));
|
|
if (parsePtr < parseEnd && *parsePtr == quote) {
|
|
++parsePtr;
|
|
}
|
|
} else {
|
|
s = new GString();
|
|
}
|
|
return s;
|
|
}
|
|
|
|
void ZxDoc::parseSpace() {
|
|
while (parsePtr < parseEnd && (*parsePtr == '\x20' ||
|
|
*parsePtr == '\x09' ||
|
|
*parsePtr == '\x0d' ||
|
|
*parsePtr == '\x0a')) {
|
|
++parsePtr;
|
|
}
|
|
}
|
|
|
|
bool ZxDoc::match(const char *s) {
|
|
int n;
|
|
|
|
n = (int)strlen(s);
|
|
return parseEnd - parsePtr >= n && !strncmp(parsePtr, s, n);
|
|
}
|
|
|
|
bool ZxDoc::write(ZxWriteFunc writeFunc, void *stream) {
|
|
ZxNode *child;
|
|
|
|
for (child = getFirstChild(); child; child = child->getNextChild()) {
|
|
if (!child->write(writeFunc, stream)) {
|
|
return false;
|
|
}
|
|
if (!(*writeFunc)(stream, "\n", 1)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
ZxXMLDecl::ZxXMLDecl(GString *versionA, GString *encodingA, bool standaloneA) {
|
|
version = versionA;
|
|
encoding = encodingA;
|
|
standalone = standaloneA;
|
|
}
|
|
|
|
ZxXMLDecl::~ZxXMLDecl() {
|
|
delete version;
|
|
if (encoding) {
|
|
delete encoding;
|
|
}
|
|
}
|
|
|
|
bool ZxXMLDecl::write(ZxWriteFunc writeFunc, void *stream) {
|
|
GString *s;
|
|
bool ok;
|
|
|
|
s = new GString("<?xml version=\"");
|
|
s->append(version);
|
|
s->append("\"");
|
|
if (encoding) {
|
|
s->append(" encoding=\"");
|
|
s->append(encoding);
|
|
s->append("\"");
|
|
}
|
|
if (standalone) {
|
|
s->append(" standlone=\"yes\"");
|
|
}
|
|
s->append("?>");
|
|
ok = (*writeFunc)(stream, s->getCString(), s->getLength());
|
|
delete s;
|
|
return ok;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
ZxDocTypeDecl::ZxDocTypeDecl(GString *nameA) {
|
|
name = nameA;
|
|
}
|
|
|
|
ZxDocTypeDecl::~ZxDocTypeDecl() {
|
|
delete name;
|
|
}
|
|
|
|
bool ZxDocTypeDecl::write(ZxWriteFunc writeFunc, void *stream) {
|
|
GString *s;
|
|
bool ok;
|
|
|
|
s = new GString("<!DOCTYPE ");
|
|
s->append(name);
|
|
s->append(">");
|
|
ok = (*writeFunc)(stream, s->getCString(), s->getLength());
|
|
delete s;
|
|
return ok;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
ZxComment::ZxComment(GString *textA) {
|
|
text = textA;
|
|
}
|
|
|
|
ZxComment::~ZxComment() {
|
|
delete text;
|
|
}
|
|
|
|
bool ZxComment::write(ZxWriteFunc writeFunc, void *stream) {
|
|
GString *s;
|
|
bool ok;
|
|
|
|
s = new GString("<!--");
|
|
s->append(text);
|
|
s->append("-->");
|
|
ok = (*writeFunc)(stream, s->getCString(), s->getLength());
|
|
delete s;
|
|
return ok;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
ZxPI::ZxPI(GString *targetA, GString *textA) {
|
|
target = targetA;
|
|
text = textA;
|
|
}
|
|
|
|
ZxPI::~ZxPI() {
|
|
delete target;
|
|
delete text;
|
|
}
|
|
|
|
bool ZxPI::write(ZxWriteFunc writeFunc, void *stream) {
|
|
GString *s;
|
|
bool ok;
|
|
|
|
s = new GString("<?");
|
|
s->append(target);
|
|
s->append(" ");
|
|
s->append(text);
|
|
s->append("?>");
|
|
ok = (*writeFunc)(stream, s->getCString(), s->getLength());
|
|
delete s;
|
|
return ok;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
ZxElement::ZxElement(GString *typeA) {
|
|
type = typeA;
|
|
attrs = new GHash();
|
|
firstAttr = lastAttr = NULL;
|
|
}
|
|
|
|
ZxElement::~ZxElement() {
|
|
delete type;
|
|
deleteGHash(attrs, ZxAttr);
|
|
}
|
|
|
|
bool ZxElement::isElement(const char *typeA) {
|
|
return !type->cmp(typeA);
|
|
}
|
|
|
|
ZxAttr *ZxElement::findAttr(const char *attrName) {
|
|
return (ZxAttr *)attrs->lookup(attrName);
|
|
}
|
|
|
|
void ZxElement::addAttr(ZxAttr *attr) {
|
|
attrs->add(attr->getName(), attr);
|
|
if (lastAttr) {
|
|
lastAttr->next = attr;
|
|
lastAttr= attr;
|
|
} else {
|
|
firstAttr = lastAttr = attr;
|
|
}
|
|
attr->parent = this;
|
|
attr->next = NULL;
|
|
}
|
|
|
|
bool ZxElement::write(ZxWriteFunc writeFunc, void *stream) {
|
|
GString *s;
|
|
ZxAttr *attr;
|
|
ZxNode *child;
|
|
bool ok;
|
|
|
|
s = new GString("<");
|
|
s->append(type);
|
|
for (attr = firstAttr; attr; attr = attr->getNextAttr()) {
|
|
s->append(" ");
|
|
s->append(attr->name);
|
|
s->append("=\"");
|
|
appendEscapedAttrValue(s, attr->value);
|
|
s->append("\"");
|
|
}
|
|
if ((child = getFirstChild())) {
|
|
s->append(">");
|
|
} else {
|
|
s->append("/>");
|
|
}
|
|
ok = (*writeFunc)(stream, s->getCString(), s->getLength());
|
|
delete s;
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
if (child) {
|
|
for (; child; child = child->getNextChild()) {
|
|
if (!child->write(writeFunc, stream)) {
|
|
return false;
|
|
}
|
|
}
|
|
s = new GString();
|
|
s->append("</");
|
|
s->append(type);
|
|
s->append(">");
|
|
ok = (*writeFunc)(stream, s->getCString(), s->getLength());
|
|
delete s;
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ZxElement::appendEscapedAttrValue(GString *out, GString *s) {
|
|
char c;
|
|
int i;
|
|
|
|
for (i = 0; i < s->getLength(); ++i) {
|
|
c = s->getChar(i);
|
|
if (c == '<') {
|
|
out->append("<");
|
|
} else if (c == '>') {
|
|
out->append(">");
|
|
} else if (c == '&') {
|
|
out->append("&");
|
|
} else if (c == '"') {
|
|
out->append(""");
|
|
} else {
|
|
out->append(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
ZxAttr::ZxAttr(GString *nameA, GString *valueA) {
|
|
name = nameA;
|
|
value = valueA;
|
|
parent = NULL;
|
|
next = NULL;
|
|
}
|
|
|
|
ZxAttr::~ZxAttr() {
|
|
delete name;
|
|
delete value;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
ZxCharData::ZxCharData(GString *dataA, bool parsedA) {
|
|
data = dataA;
|
|
parsed = parsedA;
|
|
}
|
|
|
|
ZxCharData::~ZxCharData() {
|
|
delete data;
|
|
}
|
|
|
|
bool ZxCharData::write(ZxWriteFunc writeFunc, void *stream) {
|
|
GString *s;
|
|
char c;
|
|
int i;
|
|
bool ok;
|
|
|
|
s = new GString();
|
|
if (parsed) {
|
|
for (i = 0; i < data->getLength(); ++i) {
|
|
c = data->getChar(i);
|
|
if (c == '<') {
|
|
s->append("<");
|
|
} else if (c == '>') {
|
|
s->append(">");
|
|
} else if (c == '&') {
|
|
s->append("&");
|
|
} else {
|
|
s->append(c);
|
|
}
|
|
}
|
|
} else {
|
|
s->append("<![CDATA[");
|
|
s->append(data);
|
|
s->append("]]>");
|
|
}
|
|
ok = (*writeFunc)(stream, s->getCString(), s->getLength());
|
|
delete s;
|
|
return ok;
|
|
}
|