1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* XmlParser.cc wrapper for XML I/O
4 * Copyright (C) 2000-2002 Ximian, Inc.
5 * Copyright (C) 2005 SUSE Linux Products GmbH
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License,
9 * version 2, as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25 #include <zypp/solver/detail/XmlParser.h>
26 #include <zypp/solver/detail/Dependency.h>
27 #include <zypp/solver/detail/OrDependency.h>
28 #include <zypp/solver/detail/PackageUpdate.h>
29 #include <zypp/solver/detail/utils.h>
31 #include <zypp/solver/detail/debug.h>
32 #include <zypp/ResObject.h>
34 /////////////////////////////////////////////////////////////////////////
36 { ///////////////////////////////////////////////////////////////////////
37 ///////////////////////////////////////////////////////////////////////
39 { /////////////////////////////////////////////////////////////////////
40 /////////////////////////////////////////////////////////////////////
42 { ///////////////////////////////////////////////////////////////////
46 //---------------------------------------------------------------------------
49 parse_dep_attrs(bool *is_obsolete, const xmlChar **attrs)
52 bool op_present = false;
53 /* Temporary variables dependent upon the presense of an 'op' attribute */
54 const char *name = NULL;
55 int epoch = Edition::noepoch;
59 Relation relation = Relation::Any;
63 for (i = 0; attrs[i]; i++) {
64 const char *attr = (const char *)attrs[i++];
65 const char *value = (const char *)attrs[i];
67 if (!strcasecmp(attr, "name")) name = value;
68 else if (!strcasecmp(attr, "op")) { op_present = true; relation = Relation::parse(value); }
69 else if (!strcasecmp(attr, "epoch")) epoch = atoi (value);
70 else if (!strcasecmp(attr, "version")) version = value;
71 else if (!strcasecmp(attr, "release")) release = value;
72 else if (!strcasecmp(attr, "arch")) arch = value;
73 else if (!strcasecmp (attr, "obsoletes")) *is_obsolete = true;
75 if (getenv ("RC_SPEW_XML")) rc_debug (RC_DEBUG_LEVEL_ALWAYS, "! Unknown attribute: %s = %s", attr, value);
80 /* FIXME: should get Channel from XML */
81 /* FIXME: should get Kind from XML */
82 if ( std::strlen(arch.c_str()) > 0)
84 return new Dependency (name, relation, ResTraits<zypp::Package>::kind, new Channel(CHANNEL_TYPE_ANY), epoch, version, release, zypp::Arch(arch));
88 return new Dependency (name, relation, ResTraits<zypp::Package>::kind, new Channel(CHANNEL_TYPE_ANY), epoch, version, release, zypp::Arch_noarch);
93 //---------------------------------------------------------------------------
97 sax_start_document(void *ptr)
99 XmlParser *ctx = (XmlParser *)ptr;
100 if (ctx->processing()) return;
102 // if (getenv ("RC_SPEW_XML")) rc_debug (RC_DEBUG_LEVEL_ALWAYS, "* Start document");
104 ctx->setProcessing (true);
109 sax_end_document(void *ptr)
111 XmlParser *ctx = (XmlParser *)ptr;
112 if (!ctx->processing()) return;
114 // if (getenv ("RC_SPEW_XML")) rc_debug (RC_DEBUG_LEVEL_ALWAYS, "* End document");
116 ctx->setProcessing (false);
121 sax_start_element(void *ptr, const xmlChar *name, const xmlChar **attrs)
123 XmlParser *ctx = (XmlParser *)ptr;
125 ctx->releaseBuffer();
128 // if (getenv ("RC_SPEW_XML")) rc_debug (RC_DEBUG_LEVEL_ALWAYS, "* Start element (%s)", (const char *)name);
131 for (int i = 0; attrs[i]; i += 2) {
132 if (getenv ("RC_SPEW_XML")) rc_debug (RC_DEBUG_LEVEL_ALWAYS, " - Attribute (%s=%s)", (const char *)attrs[i], (const char *)attrs[i+1]);
136 if (!strcmp((const char *)name, "channel") || !strcmp((const char *)name, "subchannel")) {
137 /* Unneeded container tags. Ignore */
141 return ctx->startElement ((const char *)name, attrs);
147 sax_end_element(void *ptr, const xmlChar *name)
149 XmlParser *ctx = (XmlParser *)ptr;
151 // if (getenv ("RC_SPEW_XML")) rc_debug (RC_DEBUG_LEVEL_ALWAYS, "* End element (%s)", (const char *)name);
153 if (!strcmp((const char *)name, "channel") || !strcmp((const char *)name, "subchannel")) {
154 /* Unneeded container tags. Ignore */
158 return ctx->endElement ((const char *)name);
163 sax_characters(void *ptr, const xmlChar *ch, int len)
165 XmlParser *ctx = (XmlParser *)ptr;
167 ctx->toBuffer ((const char *)ch, len);
173 sax_warning(void *ptr, const char *msg, ...)
180 if (vsnprintf(tmp, 2048, msg, args) >= 2048) fprintf (stderr, "vsnprintf overflow\n");
181 rc_debug (RC_DEBUG_LEVEL_WARNING, "* SAX Warning: %s", tmp);
188 sax_error(void *ptr, const char *msg, ...)
195 if (vsnprintf(tmp, 2048, msg, args) >= 2048) fprintf (stderr, "vsnprintf overflow\n");
196 rc_debug (RC_DEBUG_LEVEL_ERROR, "* SAX Error: %s", tmp);
202 static xmlSAXHandler sax_handler = {
203 NULL, /* internalSubset */
204 NULL, /* isStandalone */
205 NULL, /* hasInternalSubset */
206 NULL, /* hasExternalSubset */
207 NULL, /* resolveEntity */
208 NULL, /* getEntity */
209 NULL, /* entityDecl */
210 NULL, /* notationDecl */
211 NULL, /* attributeDecl */
212 NULL, /* elementDecl */
213 NULL, /* unparsedEntityDecl */
214 NULL, /* setDocumentLocator */
215 sax_start_document, /* startDocument */
216 sax_end_document, /* endDocument */
217 sax_start_element, /* startElement */
218 sax_end_element, /* endElement */
219 NULL, /* reference */
220 sax_characters, /* characters */
221 NULL, /* ignorableWhitespace */
222 NULL, /* processingInstruction */
224 sax_warning, /* warning */
225 sax_error, /* error */
226 sax_error /* fatalError */
229 //---------------------------------------------------------------------------
231 XmlParser::XmlParser (constChannelPtr channel)
233 , _processing (false)
234 , _xml_context (NULL)
235 , _state (PARSER_TOPLEVEL)
236 , _current_package (NULL)
237 , _current_update (NULL)
238 , _toplevel_dep_list (NULL)
239 , _current_dep_list (NULL)
240 , _text_buffer (NULL)
241 , _text_buffer_size (0)
243 // if (getenv ("RC_SPEW_XML")) rc_debug (RC_DEBUG_LEVEL_ALWAYS, "* Context created (%p)", this);
247 XmlParser::~XmlParser()
252 //---------------------------------------------------------------------------
255 XmlParser::asString ( void ) const
257 return toString (*this);
262 XmlParser::toString ( const XmlParser & context )
264 return "<XmlParser/>";
269 XmlParser::dumpOn( ostream & str ) const
277 operator<<( ostream& os, const XmlParser& context)
279 return os << context.asString();
282 //---------------------------------------------------------------------------
285 XmlParser::toBuffer (const char *data, size_t size)
287 _text_buffer = (char *)realloc (_text_buffer, _text_buffer_size + size + 1);
288 strncpy (_text_buffer + _text_buffer_size, (char *)data, size);
289 _text_buffer_size += size;
290 _text_buffer[_text_buffer_size] = 0;
292 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser[%p]::toBuffer(%.32s...,%ld)\n", this, data, (long)size);
297 XmlParser::releaseBuffer ()
302 _text_buffer_size = 0;
303 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser[%p]::releaseBuffer()\n", this);
308 XmlParser::parseChunk(const char *xmlbuf, size_t size)
310 if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::parseChunk(%.32s...,%ld)\n", xmlbuf, (long)size);
312 xmlSubstituteEntitiesDefault(true);
315 _xml_context = xmlCreatePushParserCtxt(&sax_handler, this, NULL, 0, NULL);
318 xmlParseChunk(_xml_context, xmlbuf, size, 0);
325 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::done()\n");
328 xmlParseChunk(_xml_context, NULL, 0, 1);
331 xmlFreeParserCtxt(_xml_context);
333 if (_current_package) {
334 fprintf (stderr, "Incomplete package lost\n");
337 if (_current_update) {
338 fprintf (stderr, "Incomplete update lost");
341 return _all_packages;
345 //---------------------------------------------------------------------------
346 // Parser state callbacks
349 XmlParser::startElement(const char *name, const xmlChar **attrs)
351 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::startElement(%s)\n", name);
354 case PARSER_TOPLEVEL:
355 toplevelStart(name, attrs);
358 packageStart(name, attrs);
361 historyStart(name, attrs);
364 dependencyStart(name, attrs);
375 XmlParser::endElement(const char *name)
377 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::endElement(%s)\n", name);
379 if (name != NULL) { // sax_end_element might set name to NULL
405 XmlParser::toplevelStart(const char * name, const xmlChar **attrs)
407 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::toplevelStart(%s)\n", name);
409 if (!strcmp(name, "package")) {
410 assert(_current_package == NULL);
412 _state = PARSER_PACKAGE;
414 _current_package = new Package(_channel);
415 _current_requires.clear();
416 _current_provides.clear();
417 _current_conflicts.clear();
418 _current_children.clear();
419 _current_recommends.clear();
420 _current_suggests.clear();
421 _current_obsoletes.clear();
425 if (getenv ("RC_SPEW_XML")) rc_debug (RC_DEBUG_LEVEL_ALWAYS, "! Not handling %s at toplevel", (const char *)name);
431 XmlParser::packageStart(const char * name, const xmlChar **attrs)
433 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::packageStart(%s)\n", name);
435 assert(_current_package != NULL);
437 /* Only care about the containers here */
438 if (!strcmp((const char *)name, "history")) {
439 _state = PARSER_HISTORY;
441 else if (!strcmp (name, "deps")) {
443 * We can get a <deps> tag surrounding the actual package
444 * dependency sections (requires, provides, conflicts, etc).
445 * In this case, we'll just ignore this tag quietly.
448 else if (!strcmp(name, "requires")) {
450 _current_dep_list = _toplevel_dep_list = &_current_requires;
452 else if (!strcmp(name, "recommends")) {
454 _current_dep_list = _toplevel_dep_list = &_current_recommends;
456 else if (!strcmp(name, "suggests")) {
458 _current_dep_list = _toplevel_dep_list = &_current_suggests;
460 else if (!strcmp(name, "conflicts")) {
461 bool is_obsolete = false;
466 for (i = 0; attrs && attrs[i] && !is_obsolete; i += 2) {
468 if (!strcasecmp ((const char *)(attrs[i]), "obsoletes"))
473 _current_dep_list = _toplevel_dep_list = &_current_obsoletes;
475 _current_dep_list = _toplevel_dep_list = &_current_conflicts;
478 else if (!strcmp(name, "obsoletes")) {
480 _current_dep_list = _toplevel_dep_list = &_current_obsoletes;
482 else if (!strcmp(name, "provides")) {
484 _current_dep_list = _toplevel_dep_list = &_current_provides;
486 else if (!strcmp(name, "children")) {
488 _current_dep_list = _toplevel_dep_list = &_current_children;
491 // if (getenv ("RC_SPEW_XML")) rc_debug (RC_DEBUG_LEVEL_ALWAYS, "! Not handling %s in package start", name);
497 XmlParser::historyStart(const char * name, const xmlChar **attrs)
499 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::historyStart(%s)\n", name);
501 assert(_current_package != NULL);
503 if (!strcmp(name, "update")) {
504 assert(_current_update == NULL);
506 _current_update = new PackageUpdate(_current_package->name());
508 _state = PARSER_UPDATE;
511 if (getenv ("RC_SPEW_XML")) rc_debug (RC_DEBUG_LEVEL_ALWAYS, "! Not handling %s in history", name);
517 XmlParser::dependencyStart(const char *name, const xmlChar **attrs)
519 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::dependencyStart(%s)\n", name);
521 if (!strcmp(name, "dep")) {
525 dep = parse_dep_attrs(&is_obsolete, attrs);
528 _current_obsoletes.push_back (dep);
530 _current_dep_list->push_back (dep);
533 else if (!strcmp(name, "or"))
534 _current_dep_list = new CDependencyList;
536 if (getenv ("RC_SPEW_XML")) rc_debug (RC_DEBUG_LEVEL_ALWAYS, "! Not handling %s in dependency", name);
541 //---------------------------------------------------------------------------
545 XmlParser::packageEnd(const char *name)
547 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::packageEnd(%s)\n", name);
549 assert(_current_package != NULL);
551 if (!strcmp(name, "package")) {
552 PackageUpdatePtr update;
554 /* If possible, grab the version info from the most recent update.
555 * Otherwise, try to find where the package provides itself and use
558 update = _current_package->getLatestUpdate();
561 _current_package->setName (update->name());
562 _current_package->setKind (update->kind());
563 _current_package->setEdition (update->edition());
564 _current_package->setFileSize (update->packageSize());
565 _current_package->setInstalledSize (update->installedSize());
568 for (CDependencyList::const_iterator iter = _current_provides.begin(); iter != _current_provides.end(); iter++) {
569 if ((*iter)->relation().isEqual()
570 && ((*iter)->name() == _current_package->name()))
572 _current_package->setKind ((*iter)->kind());
573 _current_package->setEdition ((*iter)->edition());
580 /* Hack for the old XML */
581 if (_current_package->arch()->isUnknown()) {
582 _current_package->setArch (zypp::Arch::System);
587 // check if we provide ourselfs properly
589 CDependencyList::const_iterator piter;
590 for (piter = _current_provides.begin(); piter != _current_provides.end(); piter++) {
591 if ((*piter)->relation().isEqual()
592 && ((*piter)->name() == _current_package->name()))
598 if (piter == _current_provides.end()) { // no self provide found, construct one
599 constDependencyPtr selfdep = new Dependency (_current_package->name(), Relation::Equal, _current_package->kind(), _current_package->channel(), _current_package->edition());
600 //if (getenv ("RC_SPEW")) fprintf (stderr, "Adding self-provide [%s]\n", selfdep->asString().c_str());
601 _current_provides.push_front (selfdep);
604 _current_package->setRequires (_current_requires);
605 _current_package->setProvides (_current_provides);
606 _current_package->setConflicts (_current_conflicts);
607 _current_package->setObsoletes (_current_obsoletes);
608 _current_package->setSuggests (_current_suggests);
609 _current_package->setRecommends (_current_recommends);
611 _all_packages.push_back (_current_package);
613 if (getenv ("RC_SPEW")) fprintf (stderr, "%s\n", _current_package->asString(true).c_str());
614 if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::packageEnd done: '%s'\n", _current_package->asString(true).c_str());
615 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::packageEnd now %ld packages\n", _all_packages.size());
616 _current_package = NULL;
617 _state = PARSER_TOPLEVEL;
619 else if (!strcmp(name, "name")) { _current_package->setName (strstrip (_text_buffer));
620 } else if (!strcmp(name, "pretty_name")) { _current_package->setPrettyName (strstrip (_text_buffer));
621 } else if (!strcmp(name, "summary")) { _current_package->setSummary (strstrip (_text_buffer));
622 } else if (!strcmp(name, "description")) { _current_package->setDescription (strstrip (_text_buffer));
623 } else if (!strcmp(name, "section")) { _current_package->setSection (new Section(strstrip (_text_buffer)));
624 } else if (!strcmp(name, "arch")) { _current_package->setArch (strstrip (_text_buffer));
625 } else if (!strcmp(name, "filesize")) { _current_package->setFileSize (atoi(_text_buffer));
626 } else if (!strcmp(name, "installedsize")) { _current_package->setInstalledSize (atoi(_text_buffer));
627 } else if (!strcmp(name, "install_only")) { _current_package->setInstallOnly (true);
628 } else if (!strcmp(name, "package_set")) { _current_package->setPackageSet (true);
630 if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::packageEnd(%s) unknown\n", name);
633 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::packageEnd(%s) done\n", name);
640 XmlParser::historyEnd(const char *name)
642 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::historyEnd(%s)\n", name);
643 assert(_current_package != NULL);
645 if (!strcmp(name, "history")) {
646 assert(_current_update == NULL);
648 _state = PARSER_PACKAGE;
654 XmlParser::updateEnd(const char *name)
656 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::updateEnd(%s)\n", name);
658 constChannelPtr channel;
659 const char *url_prefix = NULL;
661 assert(_current_package != NULL);
662 assert(_current_update != NULL);
664 channel = _current_package->channel();
666 if (channel != NULL) {
667 url_prefix = channel->filePath ();
670 if (!strcmp(name, "update")) {
671 _current_package->addUpdate(_current_update);
673 _current_update = NULL;
674 _state = PARSER_HISTORY;
676 } else if (!strcmp(name, "epoch")) { _current_update->setEpoch (atoi(_text_buffer));
677 } else if (!strcmp(name, "version")) { _current_update->setVersion (strstrip (_text_buffer));
678 } else if (!strcmp(name, "release")) { _current_update->setRelease (strstrip (_text_buffer));
679 } else if (!strcmp(name, "arch")) { _current_update->setArch (strstrip (_text_buffer));
680 } else if (!strcmp(name, "filename")) {
681 strstrip (_text_buffer);
683 _current_update->setPackageUrl (maybe_merge_paths(url_prefix, _text_buffer));
686 _current_update->setPackageUrl (_text_buffer);
688 } else if (!strcmp(name, "filesize")) { _current_update->setPackageSize (atoi(_text_buffer));
689 } else if (!strcmp(name, "installedsize")) { _current_update->setInstalledSize (atoi (_text_buffer));
690 } else if (!strcmp(name, "signaturename")) {
691 strstrip (_text_buffer);
693 _current_update->setSignatureUrl (maybe_merge_paths(url_prefix, _text_buffer));
696 _current_update->setSignatureUrl (_text_buffer);
698 } else if (!strcmp(name, "signaturesize")) { _current_update->setSignatureSize (atoi (_text_buffer));
699 } else if (!strcmp(name, "md5sum")) { _current_update->setMd5sum (strstrip (_text_buffer));
700 } else if (!strcmp(name, "importance")) { _current_update->setImportance (new Importance (strstrip (_text_buffer)));
701 } else if (!strcmp(name, "description")) { _current_update->setDescription (strstrip (_text_buffer));
702 } else if (!strcmp(name, "hid")) { _current_update->setHid (atoi(_text_buffer));
703 } else if (!strcmp (name, "license")) { _current_update->setLicense (strstrip (_text_buffer));
705 fprintf (stderr, "XmlParser::updateEnd(%s) unknown\n", name);
708 // if (_current_update != NULL && getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::updateEnd(%s) => '%s'\n", name, _current_update->asString().c_str());
716 XmlParser::dependencyEnd(const char *name)
718 // if (getenv ("RC_SPEW_XML")) fprintf (stderr, "XmlParser::dependencyEnd(%s)\n", name);
720 if (!strcmp(name, "or")) {
721 OrDependencyPtr or_dep = OrDependency::fromDependencyList (*_current_dep_list);
722 DependencyPtr dep = new Dependency (or_dep);
724 (*_current_dep_list).clear();
726 (*_toplevel_dep_list).push_back (dep);
727 _current_dep_list = _toplevel_dep_list;
729 else if (!strcmp(name, "dep")) {
730 /* We handled everything we needed for dep in start */
733 /* All of the dep lists (requires, provides, etc.) */
734 _toplevel_dep_list = NULL;
735 _current_dep_list = NULL;
736 _state = PARSER_PACKAGE;
742 //===================================================================================================================
745 //---------------------------------------------------------------------------
750 static RCResItemDep *
751 rc_xml_node_to_resItem_dep_internal (const xmlNode *node)
753 gchar *name = NULL, *version = NULL, *release = NULL;
754 gboolean has_epoch = false;
756 RCResItemRelation relation;
761 if (g_strcasecmp (node->name, "dep")) {
765 name = xml_get_prop (node, "name");
766 tmp = xml_get_prop (node, "op");
768 relation = rc_resItem_relation_from_string (tmp);
770 has_epoch = xml_get_guint32_value (node, "epoch", &epoch);
772 version = xml_get_prop (node, "version");
773 release = xml_get_prop (node, "release");
775 relation = RC_RELATION_ANY;
778 /* FIXME: should get channel from XML */
779 dep = rc_resItem_dep_new (name, has_epoch, epoch, version, release,
780 relation, RC_TYPE_RESOLVABLE, RC_CHANNEL_ANY,
789 } /* rc_xml_node_to_resItem_dep_internal */
792 rc_xml_node_to_resItem_dep (const xmlNode *node)
794 RCResItemDep *dep = NULL;
796 if (!g_strcasecmp (node->name, "dep")) {
797 dep = rc_xml_node_to_resItem_dep_internal (node);
799 } else if (!g_strcasecmp (node->name, "or")) {
800 RCResItemDepSList *or_dep_slist = NULL;
802 xmlNode *iter = node->xmlChildrenNode;
805 if (iter->type == XML_ELEMENT_NODE) {
806 or_dep_slist = g_slist_append(
808 rc_xml_node_to_resItem_dep_internal (iter));
814 or = rc_dep_or_new (or_dep_slist);
815 dep = rc_dep_or_new_provide (or);
819 } /* rc_xml_node_to_resItem_dep */
823 /* This hack cleans 8-bit characters out of a string. This is a very
824 problematic "solution" to the problem of non-UTF-8 package info. */
826 sanitize_string (const char *str)
828 gchar *dup = g_strdup (str);
834 for (c = dup; *c; ++c) {
835 if ((guint)*c > 0x7f)
844 ///////////////////////////////////////////////////////////////////
845 };// namespace detail
846 /////////////////////////////////////////////////////////////////////
847 /////////////////////////////////////////////////////////////////////
848 };// namespace solver
849 ///////////////////////////////////////////////////////////////////////
850 ///////////////////////////////////////////////////////////////////////
852 /////////////////////////////////////////////////////////////////////////