string::size_type SimpleXMLReader::loadAttribs(const string& name, const string& tmp, string::size_type start) throw(SimpleXMLException) { string::size_type i = start; string::size_type j; for(;;) { if((j = tmp.find('=', i)) == string::npos) { throw SimpleXMLException("Missing '=' in " + name); } if(tmp[j+1] != '"' && tmp[j+1] != '\'') { throw SimpleXMLException("Invalid character after '=' in " + name); } string::size_type x = j + 2; string::size_type y; if((y = tmp.find(tmp[j+1], x)) == string::npos) { throw SimpleXMLException("Missing '" + string(1, tmp[j+1]) + "' in " + name); } // Ok, we have an attribute... attribs.push_back(make_pair(tmp.substr(i, j-i), tmp.substr(x, y-x))); SimpleXML::escape(attribs.back().second, true, true, utf8); i = tmp.find_first_not_of(' ', y + 1); if(tmp[i] == '/' || tmp[i] == '>') { return i; } } }
void SimpleXML::addTag(const string& aName, const string& aData /* = "" */) { if(aName.empty()) { throw SimpleXMLException("Empty tag names not allowed"); } if(current == &root && !current->children.empty()) { throw SimpleXMLException("Only one root tag allowed"); } else { current->children.push_back(new Tag(aName, aData, current)); currentChild = current->children.end() - 1; } }
void SimpleXML::fromXML(const string& aXML) { if(!root.children.empty()) { delete root.children[0]; root.children.clear(); } TagReader t(&root); SimpleXMLReader(&t).parse(aXML.c_str(), aXML.size(), false); if(root.children.size() != 1) { throw SimpleXMLException("Invalid XML file, missing or multiple root tags"); } current = &root; resetCurrentChild(); }
void ListLoader::startTag(const string& name, StringPairList& attribs, bool simple) { if(inListing) { if(name == sFile) { const string& n = getAttrib(attribs, sName, 0); if(n.empty()) return; const string& s = getAttrib(attribs, sSize, 1); if(s.empty()) return; int64_t size = Util::toInt64(s); const string& h = getAttrib(attribs, sTTH, 2); if(h.empty()) return; TTHValue tth(h); /// @todo verify validity? if(updating) { // just update the current file if it is already there. for(DirectoryListing::File::Iter i = cur->files.begin(), iend = cur->files.end(); i != iend; ++i) { DirectoryListing::File& file = **i; /// @todo comparisons should be case-insensitive but it takes too long - add a cache if(file.getTTH() == tth || file.getName() == n) { file.setName(n); file.setSize(size); file.setTTH(tth); return; } } } DirectoryListing::File* f = new DirectoryListing::File(cur, n, size, tth); string l_ts = ""; if (!m_is_first_check_mediainfo_list){ m_is_first_check_mediainfo_list = true; l_ts = getAttrib(attribs, sTS, 3); m_is_mediainfo_list = !l_ts.empty(); } else if (m_is_mediainfo_list) { l_ts = getAttrib(attribs, sTS, 3); } if (!l_ts.empty()){ f->mediaInfo.video_info = getAttrib(attribs, sMVideo, 3); f->mediaInfo.audio_info = getAttrib(attribs, sMAudio, 3); f->mediaInfo.resolution = getAttrib(attribs, sWH, 3); f->mediaInfo.bitrate = atoi(getAttrib(attribs, sBR, 4).c_str()); } cur->files.push_back(f); } else if(name == sDirectory) { const string& n = getAttrib(attribs, sName, 0); if(n.empty()) { throw SimpleXMLException(_("Directory missing name attribute")); } bool incomp = getAttrib(attribs, sIncomplete, 1) == "1"; DirectoryListing::Directory* d = NULL; if(updating) { for(DirectoryListing::Directory::Iter i = cur->directories.begin(); i != cur->directories.end(); ++i) { /// @todo comparisons should be case-insensitive but it takes too long - add a cache if((*i)->getName() == n) { d = *i; if(!d->getComplete()) d->setComplete(!incomp); break; } } } if(d == NULL) { d = new DirectoryListing::Directory(cur, n, false, !incomp); cur->directories.push_back(d); } cur = d; if(simple) { // To handle <Directory Name="..." /> endTag(name, Util::emptyString); } } } else if(name == sFileListing) { const string& b = getAttrib(attribs, sBase, 2); if(!b.empty() && b[0] == '/' && b[b.size()-1] == '/') { base = b; } StringList sl = StringTokenizer<string>(base.substr(1), '/').getTokens(); for(StringIter i = sl.begin(); i != sl.end(); ++i) { DirectoryListing::Directory* d = NULL; for(DirectoryListing::Directory::Iter j = cur->directories.begin(); j != cur->directories.end(); ++j) { if((*j)->getName() == *i) { d = *j; break; } } if(d == NULL) { d = new DirectoryListing::Directory(cur, *i, false, false); cur->directories.push_back(d); } cur = d; } cur->setComplete(true); inListing = true; if(simple) { // To handle <Directory Name="..." /> endTag(name, Util::emptyString); } } }
void SimpleXML::addAttrib(const string& aName, const string& aData) { if(current == &root) throw SimpleXMLException("No tag is currently selected"); current->attribs.push_back(make_pair(aName, aData)); }
string::size_type SimpleXMLReader::fromXML(const string& tmp, const string& n, string::size_type start, bool inTag) throw(SimpleXMLException) { string::size_type i = start; string::size_type j; bool hasChildren = false; for(;;) { if((j = tmp.find('<', i)) == string::npos) { if(inTag) { throw SimpleXMLException("Missing end tag in " + n); } return tmp.size(); } // Check that we have at least 3 more characters as the shortest valid xml tag is <a/>... if((j + 3) > tmp.size()) { throw SimpleXMLException("Missing end tag in " + n); } i = j + 1; if(tmp[i] == '?') { // <? processing instruction ?>, check encoding... if((j = tmp.find("?>", i)) == string::npos) { throw SimpleXMLException("Missing '?>' in " + n); } string str = tmp.substr(i, j - i); if(str.find("encoding=\"utf-8\"") == string::npos) { // Ugly pass to convert from some other codepage to utf-8; note that we convert from the ACP, not the one specified in the xml... utf8 = false; } i = j + 2; continue; } if(tmp[i] == '!' && tmp[i+1] == '-' && tmp[i+2] == '-') { // <!-- comment -->, ignore... if((j = tmp.find("-->", i)) == string::npos) { throw SimpleXMLException("Missing '-->' in " + n); } i = j + 3; continue; } // Check if we reached the end tag if(tmp[i] == '/') { i++; if( (tmp.compare(i, n.length(), n) == 0) && (tmp[i + n.length()] == '>') ) { if(!hasChildren) { data = tmp.substr(start, i - start - 2); SimpleXML::escape(data, false, true, utf8); } else { data.clear(); } return i + n.length() + 1; } else { throw SimpleXMLException("Missing end tag in " + n); } } // Alright, we have a real tag for sure...now get the name of it. if((j = tmp.find_first_of(" />", i)) == string::npos) { throw SimpleXMLException("Missing '>' in " + n); } string name = tmp.substr(i, j-i); hasChildren = true; if(tmp[j] == ' ') { if((j = tmp.find_first_not_of(' ', j+1)) == string::npos) { throw SimpleXMLException("Missing '>' in " + name); } } attribs.clear(); if(tmp[j] != '/' && tmp[j] != '>') { // We have attribs... j = loadAttribs(name, tmp, j); } if(tmp[j] == '>') { // This is a real tag with data etc... cb->startTag(name, attribs, false); j = fromXML(tmp, name, j+1, true); cb->endTag(name, data); } else { // A simple tag (<xxx/> cb->startTag(name, attribs, true); j++; } i = j; } }
void ListLoader::startTag(const string& name, StringPairList& attribs, bool simple) { if(list->getAbort()) { throw AbortException(); } if(inListing) { if(name == sFile) { const string& n = getAttrib(attribs, sName, 0); if(n.empty()) return; const string& s = getAttrib(attribs, sSize, 1); if(s.empty()) return; const string& h = getAttrib(attribs, sTTH, 2); if(h.empty()) { return; } DirectoryListing::File* f = new DirectoryListing::File(cur, n, Util::toInt64(s), h); cur->files.push_back(f); } else if(name == sDirectory) { const string& n = getAttrib(attribs, sName, 0); if(n.empty()) { throw SimpleXMLException("Directory missing name attribute"); } bool incomp = getAttrib(attribs, sIncomplete, 1) == "1"; DirectoryListing::Directory* d = NULL; if(updating) { for(DirectoryListing::Directory::Iter i = cur->directories.begin(); i != cur->directories.end(); ++i) { if((*i)->getName() == n) { d = *i; if(!d->getComplete()) d->setComplete(!incomp); break; } } } if(d == NULL) { d = new DirectoryListing::Directory(cur, n, false, !incomp); cur->directories.push_back(d); } cur = d; if(simple) { // To handle <Directory Name="..." /> endTag(name, Util::emptyString); } } } else if(name == sFileListing) { const string& b = getAttrib(attribs, sBase, 2); if(b.size() >= 1 && b[0] == '/' && b[b.size()-1] == '/') { base = b; } StringList sl = StringTokenizer<string>(base.substr(1), '/').getTokens(); for(StringIter i = sl.begin(); i != sl.end(); ++i) { DirectoryListing::Directory* d = NULL; for(DirectoryListing::Directory::Iter j = cur->directories.begin(); j != cur->directories.end(); ++j) { if((*j)->getName() == *i) { d = *j; break; } } if(d == NULL) { d = new DirectoryListing::Directory(cur, *i, false, false); cur->directories.push_back(d); } cur = d; } cur->setComplete(true); inListing = true; if(simple) { // To handle <Directory Name="..." /> endTag(name, Util::emptyString); } } }