/************************************************************************ MeOS - Orienteering Software Copyright (C) 2009-2019 Melin Software HB This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Melin Software HB - software@melin.nu - www.melin.nu Eksoppsvägen 16, SE-75646 UPPSALA, Sweden ************************************************************************/ // xmlparser.cpp: implementation of the xmlparser class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "xmlparser.h" #include "meos_util.h" #include "progress.h" #include "meosexception.h" #include "gdioutput.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// extern gdioutput *gdi_main; xmlparser::xmlparser() : utfConverter(gdi_main) { progress = 0; lastIndex = 0; tagStackPointer=0; isUTF = false; cutMode = false; toString = false; } xmlparser::~xmlparser() { delete progress; fin.close(); foutFile.close(); } inline bool isBlankSpace(char b) { return b == ' ' || b == '\t' || b == '\n' || b == '\r'; } void xmlparser::setProgress(HWND hWnd) { progress = new ProgressWindow(hWnd); } void xmlparser::access(int index) { if (progress && (index-lastIndex)>1000 ) { lastIndex = index; progress->setProgress(500 + int(500.0 * index/xmlinfo.size())); } } xmlobject::xmlobject() { parser = 0; } xmlobject::~xmlobject() { //MessageBox(NULL, name.c_str(), "Destroying: ", MB_OK); // if (objects) delete objects; } const string &xmlparser::encodeXML(const wstring &input) { return ::encodeXML(gdi_main->toUTF8(input)); } const string &xmlparser::encodeXML(const string &input) { if (utfConverter) return ::encodeXML(utfConverter->toUTF8(gdi_main->widen(input))); //XXX WCS else return ::encodeXML(input); } void xmlparser::write(const char *tag, const wstring &Value) { if (!cutMode || !Value.empty()) { auto &valEnc = encodeXML(Value); if (valEnc.length() > 400) { fOut() << "<" << tag << ">" << valEnc << "" << endl; } else { char bf[512]; sprintf_s(bf, "<%s>%s\n", tag, valEnc.c_str(), tag); fOut() << bf; } /*fOut() << "<" << tag << ">" << encodeXML(Value) << "" << endl;*/ } if (!fOut().good()) throw meosException("Writing to XML file failed."); } void xmlparser::write(const char *tag, const string &Value) { if (!cutMode || Value!="") { auto &valEnc = encodeXML(Value); if (valEnc.length() > 400) { fOut() << "<" << tag << ">" << valEnc << "" << endl; } else { char bf[512]; sprintf_s(bf, "<%s>%s\n", tag, valEnc.c_str(), tag); fOut() << bf; } } if (!fOut().good()) throw meosException("Writing to XML file failed."); } void xmlparser::write(const char *tag, const char *Value) { if (!cutMode || Value != "") { auto &valEnc = encodeXML(Value); if (valEnc.length() > 400) { fOut() << "<" << tag << ">" << valEnc << "" << endl; } else { char bf[512]; sprintf_s(bf, "<%s>%s\n", tag, valEnc.c_str(), tag); fOut() << bf; } } if (!fOut().good()) throw meosException("Writing to XML file failed."); } void xmlparser::write(const char *tag) { char bf[128]; sprintf_s(bf, "<%s/>\n", tag); fOut() << bf; //fOut() << "<" << tag << "/>" << endl; if (!fOut().good()) throw meosException("Writing to XML file failed."); } void xmlparser::write(const char *tag, const char *Property, const string &Value) { if (!cutMode || Value!="") { fOut() << "<" << tag << " " << Property << "=\"" << encodeXML(Value) << "\"/>\n"; } if (!fOut().good()) throw meosException("Writing to XML file failed."); } void xmlparser::write(const char *tag, const char *Property, const wstring &Value) { if (!cutMode || !Value.empty()) { fOut() << "<" << tag << " " << Property << "=\"" << encodeXML(Value) << "\"/>\n"; } if (!fOut().good()) throw meosException("Writing to XML file failed."); } void xmlparser::write(const char *tag, const char *prop, const wchar_t *value) { encodeString = value; write(tag, prop, encodeString); } void xmlparser::writeBool(const char *tag, const char *prop, bool value) { if (!cutMode || value) write(tag, prop, value ? L"true" : L"false"); } /* void xmlparser::write(const char *tag, const char *Property, const string &PropValue, const string &Value) { if (!cutMode || Value != "" || PropValue != "") { fOut() << "<" << tag << " " << Property << "=\"" << encodeXML(PropValue) << "\">" << encodeXML(Value) << "" << endl; } if (!fOut().good()) throw meosException("Writing to XML file failed."); } */ void xmlparser::write(const char *tag, const char *Property, const wstring &PropValue, const wstring &Value) { if (!cutMode || Value != L"" || PropValue != L"") { fOut() << "<" << tag << " " << Property << "=\"" << encodeXML(PropValue) << "\">" << encodeXML(Value) << "\n"; } if (!fOut().good()) throw meosException("Writing to XML file failed."); } /* void xmlparser::write(const char *tag, const vector< pair > &propValue, const string &value) { if (!cutMode || value != "" || !propValue.empty()) { fOut() << "<" << tag; for (size_t k = 0; k < propValue.size(); k++) { fOut() << " " << propValue[k].first << "=\"" << encodeXML(propValue[k].second) << "\""; } if (!value.empty()) { fOut() << ">" << encodeXML(value) << "" << endl; } else fOut() << "/>" << endl; } if (!fOut().good()) throw meosException("Writing to XML file failed."); } void xmlparser::write(const char *tag, const vector< pair > &propValue, const wstring &value) { if (!cutMode || value != L"" || !propValue.empty()) { fOut() << "<" << tag; for (size_t k = 0; k < propValue.size(); k++) { fOut() << " " << propValue[k].first << "=\"" << encodeXML(propValue[k].second) << "\""; } if (!value.empty()) { fOut() << ">" << encodeXML(value) << "" << endl; } else fOut() << "/>" << endl; } if (!fOut().good()) throw meosException("Writing to XML file failed."); } */ void xmlparser::write(const char *tag, const vector< pair > &propValue, const wstring &value) { if (!cutMode || value != L"" || !propValue.empty()) { fOut() << "<" << tag; for (size_t k = 0; k < propValue.size(); k++) { fOut() << " " << propValue[k].first << "=\"" << encodeXML(propValue[k].second) << "\""; } if (!value.empty()) { fOut() << ">" << encodeXML(value) << "\n"; } else fOut() << "/>\n"; } if (!fOut().good()) throw meosException("Writing to XML file failed."); } /* void xmlparser::write(const char *tag, const char *prop, bool propValue, const string &value) { write(tag, prop, propValue ? "true" : "false", value); }*/ void xmlparser::writeBool(const char *tag, const char *prop, bool propValue, const wstring &value) { static const wstring wTrue = L"true"; static const wstring wFalse = L"false"; write(tag, prop, propValue ? wTrue : wFalse, value); } /* void xmlparser::write(const char *tag, const char *prop, const char *propValue, const string &value) { write(tag, prop, string(propValue), value); }*/ void xmlparser::write(const char *tag, const char *prop, const wchar_t *propValue, const wstring &value) { write(tag, prop, wstring(propValue), value); } void xmlparser::write(const char *tag, int Value) { if (!cutMode || Value!=0) { char bf[256]; sprintf_s(bf, "<%s>%d\n", tag, Value, tag); fOut() << bf; //fOut() << "<" << tag << ">" // << Value // << "" << endl; } if (!fOut().good()) throw meosException("Writing to XML file failed."); } void xmlparser::writeBool(const char *tag, bool value) { if (!cutMode || value) { if (value) { char bf[256]; sprintf_s(bf, "<%s>true\n", tag, tag); fOut() << bf; } else { char bf[256]; sprintf_s(bf, "<%s>false\n", tag, tag); fOut() << bf; } } if (!fOut().good()) throw meosException("Writing to XML file failed."); } void xmlparser::write64(const char *tag, __int64 Value) { if (!cutMode || Value!=0) { fOut() << "<" << tag << ">" << Value << "\n"; } if (!fOut().good()) throw meosException("Writing to XML file failed."); } void xmlparser::startTag(const char *tag, const char *prop, const wstring &Value) { if (tagStackPointer<32) { const string &valEnc = encodeXML(Value); if (valEnc.length() < 128) { char bf[256]; sprintf_s(bf, "<%s %s=\"%s\">\n", tag, prop, valEnc.c_str()); fOut() << bf; } else { fOut() << "<" << tag << " " << prop << "=\"" << encodeXML(Value) << "\">" << endl; } tagStack[tagStackPointer++]=tag; if (!fOut().good()) throw meosException("Writing to XML file failed."); } else throw meosException("Tag depth too large."); } void xmlparser::startTag(const char *tag, const char *prop, const string &Value) { if (tagStackPointer<32) { const string &valEnc = encodeXML(Value); if (valEnc.length() < 128) { char bf[256]; sprintf_s(bf, "<%s %s=\"%s\">\n", tag, prop, valEnc.c_str()); fOut() << bf; } else { fOut() << "<" << tag << " " << prop << "=\"" << encodeXML(Value) << "\">" << endl; } tagStack[tagStackPointer++]=tag; if (!fOut().good()) throw meosException("Writing to XML file failed."); } else throw meosException("Tag depth too large."); } /* void xmlparser::startTag(const char *tag, const vector &propvalue) { if (tagStackPointer<32) { fOut() << "<" << tag << " "; for (size_t k=0;k\n"; tagStack[tagStackPointer++]=tag; if (!fOut().good()) throw meosException("Writing to XML file failed."); } else throw meosException("Tag depth too large."); } */ void xmlparser::startTag(const char *tag, const vector &propvalue) { if (tagStackPointer<32) { fOut() << "<" << tag ; for (size_t k=0;k\n"; tagStack[tagStackPointer++]=tag; if (!fOut().good()) throw meosException("Writing to XML file failed."); } else throw meosException("Tag depth too large."); } void xmlparser::startTag(const char *tag) { if (tagStackPointer<32) { char bf[128]; sprintf_s(bf, "<%s>\n", tag); fOut() << bf; tagStack[tagStackPointer++]=tag; if (!fOut().good()) throw meosException("Writing to XML file failed."); } else throw meosException("Tag depth too large."); } void xmlparser::endTag() { if (tagStackPointer>0) { char bf[128]; const char *tag = tagStack[--tagStackPointer].c_str(); sprintf_s(bf, "\n", tag); fOut() << bf; if (!fOut().good()) throw meosException("Writing to XML file failed."); } else throw std::exception("BAD XML CODE"); } void xmlparser::openMemoryOutput(bool useCutMode) { cutMode = useCutMode; toString = true; foutString.clear(); if (utfConverter) fOut() << "\n\n\n"; else fOut() << "\n\n\n"; string out = foutString.str(); } void xmlparser::getMemoryOutput(string &res) { res = foutString.str(); foutString.clear(); } void xmlparser::openOutput(const wchar_t *file, bool useCutMode) { openOutputT(file, useCutMode, ""); } void xmlparser::openOutputT(const wchar_t *file, bool useCutMode, const string &type) { toString = false; cutMode = useCutMode; foutFile.open(file); checkWriteAccess(file); tagStackPointer=0; if (foutFile.bad()) throw meosException(wstring(L"Writing to XML file failed: ") + wstring(file)); if (utfConverter) fOut() << "\n\n\n"; else fOut() << "\n\n\n"; if (!type.empty()) { startTag(type.c_str()); } return; } int xmlparser::closeOut() { while(tagStackPointer>0) endTag(); int len = (int)foutFile.tellp(); foutFile.close(); return len; } xmldata::xmldata(const char *t, char *d) : tag(t), data(d) { parent = -1; next = 0; } xmlattrib::xmlattrib(const char *t, char *d, const xmlparser *p) : tag(t), data(d), parser(p) {} void xmlparser::read(const wstring &file, int maxobj) { fin.open(file.c_str(), ios::binary); if (!fin.good()) throw meosException(L"Failed to open 'X' for reading.#" + file); char bf[1024]; bf[0]=0; do { fin.getline(bf, 1024, '>'); lineNumber++; } while(fin.good() && bf[0]==0); char *ptr=ltrim(bf); isUTF = checkUTF(ptr); int p1 = (int)fin.tellg(); fin.seekg(0, ios::end); int p2 = (int)fin.tellg(); fin.seekg(p1, ios::beg); int asize = p2-p1; if (maxobj>0) asize = min(asize, maxobj*256); if (progress && asize>80000) progress->init(); xbf.resize(asize+1); xmlinfo.clear(); xbf.reserve(xbf.size() / 30); // Guess number of tags parseStack.clear(); fin.read(&xbf[0], xbf.size()); xbf[asize] = 0; fin.close(); parse(maxobj); } void xmlparser::readMemory(const string &mem, int maxobj) { if (mem.empty()) return; char bf[1024]; bf[0] = mem[0]; int i = 1; int stop = min(1020, mem.length()); while (i < stop && mem[i-1] != '>'){ bf[i] = mem[i]; i++; } bf[i] = 0; char *ptr=ltrim(bf); isUTF = checkUTF(ptr); int p1 = i; int p2 = mem.size(); int asize = p2-p1; if (maxobj>0) asize = min(asize, maxobj*256); if (progress && asize>80000) progress->init(); xbf.resize(asize+1); xmlinfo.clear(); xmlinfo.reserve(xbf.size() / 30); // Guess number of tags parseStack.clear(); memcpy(&xbf[0], mem.c_str() + p1, xbf.size()); xbf[asize] = 0; parse(maxobj); } bool xmlparser::checkUTF(const char *ptr) const { bool utf = false; if (ptr[0] == -17 && ptr[1]==-69 && ptr[2]==-65) { utf = true; ptr+=3; //Windows UTF attribute } if (memcmp(ptr, " 50000) { progress->setProgress(int(500.0*pp/size)); oldPrg = pp; } // Found tag if (xbf[pp] == '<') { xbf[pp] = 0; char *start = &xbf[pp+1]; while (pp < size && xbf[pp] != '>') pp++; if (xbf[pp] == '>') { xbf[pp] = 0; } if (*start=='!') continue; //Comment processTag(start, &xbf[pp-1]); } if (maxobj>0 && int(xmlinfo.size()) >= maxobj) { xbf[pp+1] = 0; return true; } pp++; } lastIndex = 0; return true; } void inplaceDecodeXML(char *in); bool xmlparser::processTag(char *start, char *end) { static char err[64]; bool onlyAttrib = *end == '/'; bool endTag = *start == '/'; char *tag = start; if (endTag) tag++; while (start<=end && !isBlankSpace(*start)) start++; *start = 0; if (!endTag && !onlyAttrib) { parseStack.push_back(xmlinfo.size()); xmlinfo.push_back(xmldata(tag, end+2)); int p = parseStack.size()-2; xmlinfo.back().parent = p>=0 ? parseStack[p] : -1; } else if (endTag) { if (!parseStack.empty()){ xmldata &xd = xmlinfo[parseStack.back()]; inplaceDecodeXML(xd.data); if (strcmp(tag, xd.tag)== 0) { parseStack.pop_back(); xd.next = xmlinfo.size(); } else { sprintf_s(err, "Unmatched tag '%s', expected '%s'.", tag, xd.tag); throw std::exception(err); } } else { sprintf_s(err, "Unmatched tag '%s'.", tag); throw std::exception(err); } } else if (onlyAttrib) { *end = 0; xmlinfo.push_back(xmldata(tag, 0)); int p = parseStack.size() - 1; xmlinfo.back().parent = p>=0 ? parseStack[p] : -1; xmlinfo.back().next = xmlinfo.size(); } return true; } char * xmlparser::ltrim(char *s) { while(*s && isspace(BYTE(*s))) s++; return s; } const char * xmlparser::ltrim(const char *s) { while(*s && isspace(BYTE(*s))) s++; return s; } /* const char * xmlparser::getError() { return errorMessage.c_str(); }*/ xmlobject xmlobject::getObject(const char *pname) const { if (pname == 0) return *this; if (isnull()) throw std::exception("Null pointer exception"); vector &xmlinfo = parser->xmlinfo; parser->access(index); unsigned child = index+1; while (child < xmlinfo.size() && xmlinfo[child].parent == index) { if (strcmp(xmlinfo[child].tag, pname)==0) return xmlobject(parser, child); else child = xmlinfo[child].next; } return xmlobject(0); } void xmlobject::getObjects(xmlList &obj) const { obj.clear(); if (isnull()) throw std::exception("Null pointer exception"); vector &xmlinfo = parser->xmlinfo; unsigned child = index+1; parser->access(index); while (child < xmlinfo.size() && xmlinfo[child].parent == index) { obj.push_back(xmlobject(parser, child)); child = xmlinfo[child].next; } } void xmlobject::getObjects(const char *tag, xmlList &obj) const { obj.clear(); if (isnull()) throw std::exception("Null pointer exception"); vector &xmlinfo = parser->xmlinfo; unsigned child = index+1; parser->access(index); while (child < xmlinfo.size() && xmlinfo[child].parent == index) { if (strcmp(tag, xmlinfo[child].tag) == 0) obj.push_back(xmlobject(parser, child)); child = xmlinfo[child].next; } } const xmlobject xmlparser::getObject(const char *pname) const { if (xmlinfo.size()>0){ if (pname == 0 || strcmp(xmlinfo[0].tag, pname) == 0) return xmlobject(const_cast(this), 0); else return xmlobject(const_cast(this), 0).getObject(pname); } else return xmlobject(0); } xmlattrib xmlobject::getAttrib(const char *pname) const { if (pname != 0) { char *start = const_cast(parser->xmlinfo[index].tag); const char *end = parser->xmlinfo[index].data; if (end) end-=2; else { if (size_t(index + 1) < parser->xmlinfo.size()) end = parser->xmlinfo[index+1].tag - 1; else end = &parser->xbf.back(); } // Scan past tag. while (startisUTF) { int wlen = MultiByteToWideChar(CP_UTF8, 0, ptr, len, buff, buff_pre_alloc); buff[wlen-1] = 0; } else { int cp = 1252; /*XXX TODO: switch(getEncoding()) { case Russian: cp = 1251; break; case EastEurope: cp = 1250; break; case Hebrew: cp = 1255; break; }*/ int wlen = MultiByteToWideChar(cp, MB_PRECOMPOSED, ptr, len, buff, buff_pre_alloc); buff[wlen-1] = 0; } return buff; } const char *xmlobject::get() const { const char *ptr = getRaw(); if (ptr == 0) return 0; static char buff[buff_pre_alloc]; if (parser->isUTF) { int len = strlen(ptr); len = min(len+1, buff_pre_alloc-10); int wlen = MultiByteToWideChar(CP_UTF8, 0, ptr, len, parser->strbuffw, buff_pre_alloc); parser->strbuffw[wlen-1] = 0; for (int k = 0; k< wlen; k++) { buff[k] = parser->strbuffw[k] & 0xFF; } } else { return ptr; } return buff; } void xmlparser::convertString(const char *in, char *out, int maxlen) const { if (in==0) throw std::exception("Null pointer exception"); if (!isUTF) { strncpy_s(out, maxlen, in, maxlen-1); out[maxlen-1] = 0; return; } wchar_t buff[buff_pre_alloc]; int len = strlen(in); len = min(min(len+1, maxlen), buff_pre_alloc-10); int wlen = MultiByteToWideChar(CP_UTF8, 0, in, len, buff, buff_pre_alloc); buff[wlen-1] = 0; BOOL untranslated = false; WideCharToMultiByte(CP_ACP, 0, buff, wlen, out, buff_pre_alloc, "?", &untranslated); out[wlen-1] = 0; if (untranslated) unconverted++; } void xmlparser::convertString(const char *in, wchar_t *out, int maxlen) const { if (in==0) throw std::exception("Null pointer exception"); int len = strlen(in); len = min(min(len+1, maxlen), buff_pre_alloc-10); if (!isUTF) { int cp = 1252; /*XXX TODO: switch(getEncoding()) { case Russian: cp = 1251; break; case EastEurope: cp = 1250; break; case Hebrew: cp = 1255; break; }*/ int wlen = MultiByteToWideChar(cp, MB_PRECOMPOSED, in, len, out, maxlen); out[wlen-1] = 0; return; } int wlen = MultiByteToWideChar(CP_UTF8, 0, in, len, out, maxlen); out[wlen-1] = 0; } bool xmlobject::getObjectBool(const char *pname) const { string tmp; getObjectString(pname, tmp); return tmp=="true" || atoi(tmp.c_str()) > 0 || _strcmpi(trim(tmp).c_str(), "true") == 0; } string &xmlobject::getObjectString(const char *pname, string &out) const { xmlobject x=getObject(pname); if (x) { const char *bf = x.getRaw(); if (bf) { parser->convertString(x.getRaw(), parser->strbuff, buff_pre_alloc); out = parser->strbuff; return out; } } xmlattrib xa(getAttrib(pname)); if (xa && xa.data) { parser->convertString(xa.get(), parser->strbuff, buff_pre_alloc); out = parser->strbuff; } else out = ""; return out; } wstring &xmlobject::getObjectString(const char *pname, wstring &out) const { xmlobject x=getObject(pname); if (x) { const wchar_t *bf = x.getw(); if (bf) { out = bf; return out; } } xmlattrib xa(getAttrib(pname)); if (xa && xa.data) { parser->convertString(xa.get(), parser->strbuffw, buff_pre_alloc); out = parser->strbuffw; } else out = L""; return out; } char *xmlobject::getObjectString(const char *pname, char *out, int maxlen) const { xmlobject x=getObject(pname); if (x) { const char *bf = x.getRaw(); if (bf) { parser->convertString(bf, out, maxlen); return out; } else out[0] = 0; } else { xmlattrib xa(getAttrib(pname)); if (xa && xa.data) { parser->convertString(xa.data, out, maxlen); inplaceDecodeXML(out); } else out[0] = 0; } return out; } wchar_t *xmlobject::getObjectString(const char *pname, wchar_t *out, int maxlen) const { xmlobject x=getObject(pname); if (x) { const char *bf = x.getRaw(); if (bf) { parser->convertString(bf, out, maxlen); return out; } else out[0] = 0; } else { xmlattrib xa(getAttrib(pname)); if (xa && xa.data) { inplaceDecodeXML(xa.data); //WCS XXX Only once!? parser->convertString(xa.data, out, maxlen); } else out[0] = 0; } return out; } const char *xmlattrib::get() const { if (data) return decodeXML(data); else return 0; } const wchar_t *xmlattrib::wget() const { if (data) { const char *dec = decodeXML(data); static wchar_t xbf[buff_pre_alloc]; parser->convertString(dec, xbf, buff_pre_alloc); return xbf; } else return 0; }