//======================================================================== // // Zoox.cc // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #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("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("') { 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("= '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("", 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("", 3)) { par->addChild(new ZxComment(new GString(start, (int)(parsePtr - start)))); parsePtr += 3; return; } ++parsePtr; } parsePtr = parseEnd; } // assumes match("", 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("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("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(""); 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("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("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("append(data); s->append("]]>"); } ok = (*writeFunc)(stream, s->getCString(), s->getLength()); delete s; return ok; }